Search code examples
javascriptthree.jsaframegltf

change position of gltf-model in a-frame scene using three.js


I have gltf-model loaded to a-frame and I want to move it by y-axis by 20. I use this code:

The problem appears in a-frame scene, actually the object is moved up (check shadows and scene inspector in "after" images), but it still displayed on it's previous position. It seems that the scene needs some kind of refresh.

The question is, how to move it properly with three.js code?
BEFORE: BEFORE1 BEFORE2


AFTER: AFTER1 AFTER2

Code:

<html>

<a-scene>
  <a-sky color="#f9f2cf"></a-sky>

  <!-- LIGHT a-frame no need to export from blender -->
  <a-light color="#fff" position="-8 5 0" intensity="3.5" light="intensity:2"></a-light>
  <a-light color="#fff" position="0 5 -14.163" intensity="3.5" light="intensity:2"></a-light>
  <a-light color="#fff" position="0 5 14.192" intensity="3.5" light="intensity:2"></a-light>
  <a-light color="#fff" position="0 -9.574 -2.443" intensity="3.5" light="intensity:2"></a-light>
  <a-light color="#fff" position="8.5 5 0" intensity="3.5" light="intensity:2"></a-light>

  <!-- CAMERA with wasd controls and circle cursor -->
  <a-camera fly look-controls wasd-controls position="17.020 16.700 7.958" rotation="-34.149 89.382 0.000">  
    <a-entity 
      cursor="fuse: true; fuseTimeout: 500"
      position="0 0 -1"
      geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
      material="color: black; shader: flat">
    </a-entity>
  </a-camera>

  <a-assets>
    <!-- GLTF animation samples -->
    <a-asset-item id="afb_animation" src="models/afb_animation.gltf"></a-asset-item>
  </a-assets>

  <a-entity id="_afb_animation" position="0 0 0"  gltf-model="#afb_animation" ></a-entity>
</a-scene>

<!-- script change model position -->
<script>

  $( document ).ready(function() {

    var xAxis = new THREE.Vector3(1,0,0);

    setTimeout(function(){
      console.log("rotation: done");

      document.querySelector('#_afb_animation').sceneEl.object3D.translateY(20);

    }, 3000);

  });
</script>


Solution

  • One way to solve your problem is using the setAttribute method (official wiki here) on the position component.

    For example:

    entity.setAttribute('position', { x: 1, y: 2, z: 3 });

    where entity is any A-Frame entity.

    Example 1

    So, in the following code I registered an A-Frame component called move-my-model that allows me to move my entity (in my case is a <a-box> entity but it will work also with your model) adding 10 to its y-position after 3000ms with setTimeout (such as your code example):

    <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
    
    <script>
    AFRAME.registerComponent('move-my-model', {
      init: function () {
        setTimeout( () => {
          let position = this.el.getAttribute("position")
          position.y += 10
          this.el.setAttribute("position", position)
        }, 3000)
      }
    })
    </script>
    
    <a-scene>
      <a-sky color="#f9f2cf"></a-sky>
    
      <!-- LIGHT a-frame no need to export from blender -->
      <a-light color="#fff" position="-8 5 0" intensity="3.5" light="intensity:2"></a-light>
      <a-light color="#fff" position="0 5 -14.163" intensity="3.5" light="intensity:2"></a-light>
      <a-light color="#fff" position="0 5 14.192" intensity="3.5" light="intensity:2"></a-light>
      <a-light color="#fff" position="0 -9.574 -2.443" intensity="3.5" light="intensity:2"></a-light>
      <a-light color="#fff" position="8.5 5 0" intensity="3.5" light="intensity:2"></a-light>
    
      <!-- CAMERA with wasd controls and circle cursor -->
      <a-camera fly look-controls wasd-controls position="17.020 16.700 7.958" rotation="-34.149 89.382 0.000">  
        <a-entity 
          cursor="fuse: true; fuseTimeout: 500"
          position="0 0 -1"
          geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
          material="color: black; shader: flat">
        </a-entity>
      </a-camera>
      
      <a-box move-my-model position="5.020 8.500 7.958" rotation="0 0 0" color="#4CC3D9"></a-box>
      
    </a-scene>

    Example 2

    Instead, if you need a more complex animation, I wrote an A-Frame component called animation-move on my own when I worked the first time on the A-Frame framework, that is:

    <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
    <script>
    AFRAME.registerComponent('animation-move', {
      schema: {
        path: {
          default: [],
          parse: function (value) {
            return JSON.parse(value);
          }
        },
        animationStepTime: {
          type: 'int',
          default: 0
        }
      },
      init: function(){
        this.next = 0;
        let object = this.el;
        for (let prop in this.data.path[this.next]) {
          object.setAttribute( prop, this.data.path[this.next][prop] );
        }
      },
      tick: function (time, timeDelta) {
        let updated = false
    
        if ( this.next >= this.data.path.length ) {
          this.next = 0;
        }
    
        let delta = this.data.animationStepTime / (16.7 * ((this.data.animationStepTime+timeDelta)/this.data.animationStepTime));
        let object = this.el;
    
        for (let prop in this.data.path[this.next]) {
    
          let attr = object.getAttribute(prop);
          let nextStep = this.data.path[this.next][prop];
    
          let xDelta = Math.abs( (this.next-1 >= 0) ? nextStep.x - this.data.path[this.next-1][prop].x : nextStep.x - this.data.path[this.data.path.length-1][prop].x)/delta;
          let yDelta = Math.abs( (this.next-1 >= 0) ? nextStep.y - this.data.path[this.next-1][prop].y : nextStep.y - this.data.path[this.data.path.length-1][prop].y)/delta;
          let zDelta = Math.abs( (this.next-1 >= 0) ? nextStep.z - this.data.path[this.next-1][prop].z : nextStep.z - this.data.path[this.data.path.length-1][prop].z)/delta;
    
          if (attr.x != nextStep.x) {
            if ((this.next-1 >= 0 && nextStep.x < this.data.path[this.next-1][prop].x) || (this.next == 0 && nextStep.x < this.data.path[this.data.path.length-1][prop].x)) {
              if (attr.x-xDelta < nextStep.x) {
                attr.x = nextStep.x;
              }
              else {
                attr.x -= xDelta;
                updated = true;
              }
            }
            else if (this.next-1 >= 0 && nextStep.x > this.data.path[this.next-1][prop].x || (this.next == 0 && nextStep.x > this.data.path[this.data.path.length-1][prop].x)) {
              if (attr.x+xDelta > nextStep.x) {
                attr.x = nextStep.x;
              }
              else {
                attr.x += xDelta;
                updated = true;
              }
            }
            else {
              attr.x = nextStep.x;
            }
          }
    
          if (attr.y != nextStep.y) {
            if (this.next-1 >= 0 && nextStep.y < this.data.path[this.next-1][prop].y || (this.next == 0 && nextStep.y < this.data.path[this.data.path.length-1][prop].y)) {
              if (attr.y-yDelta < nextStep.y) {
                attr.y = nextStep.y;
              }
              else {
                attr.y -= yDelta;
                updated = true;
              }
            }
            else if (this.next-1 >= 0 && nextStep.y > this.data.path[this.next-1][prop].y || (this.next == 0 && nextStep.y > this.data.path[this.data.path.length-1][prop].y)) {
              if (attr.y+yDelta > nextStep.y) {
                attr.y = nextStep.y;
              }
              else {
                attr.y += yDelta;
                updated = true;
              }
            }
            else {
              attr.y = nextStep.y;
            }
          }
    
          if (attr.z != nextStep.z) {
            if (this.next-1 >= 0 && nextStep.z < this.data.path[this.next-1][prop].z || (this.next == 0 && nextStep.z < this.data.path[this.data.path.length-1][prop].z)) {
              if (attr.z-zDelta < nextStep.z) {
                attr.z = nextStep.z;
              }
              else {
                attr.z -= zDelta;
                updated = true;
              }
            }
            else if (this.next-1 >= 0 && nextStep.z > this.data.path[this.next-1][prop].z || (this.next == 0 && nextStep.z > this.data.path[this.data.path.length-1][prop].z)) {
              if (attr.z+zDelta > nextStep.z) {
                attr.z = nextStep.z;
              }
              else {
                attr.z += zDelta;
                updated = true;
              }
            }
            else {
              attr.z = nextStep.z;
            }
          }
    
          object.setAttribute( prop, attr.x+' '+attr.y+' '+attr.z );
        }
        if (!updated) {
          this.next++;
        }
      }
    });
    </script>
    
    <a-scene>
      <a-sky color="#f9f2cf"></a-sky>
    
      <!-- LIGHT a-frame no need to export from blender -->
      <a-light color="#fff" position="-8 5 0" intensity="3.5" light="intensity:2"></a-light>
      <a-light color="#fff" position="0 5 -14.163" intensity="3.5" light="intensity:2"></a-light>
      <a-light color="#fff" position="0 5 14.192" intensity="3.5" light="intensity:2"></a-light>
      <a-light color="#fff" position="0 -9.574 -2.443" intensity="3.5" light="intensity:2"></a-light>
      <a-light color="#fff" position="8.5 5 0" intensity="3.5" light="intensity:2"></a-light>
    
      <!-- CAMERA with wasd controls and circle cursor -->
      <a-camera fly look-controls wasd-controls position="17.020 16.700 7.958" rotation="-34.149 89.382 0.000">  
        <a-entity 
          cursor="fuse: true; fuseTimeout: 500"
          position="0 0 -1"
          geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
          material="color: black; shader: flat">
        </a-entity>
      </a-camera>
      
      <a-box animation-move='path: [ {"position": {"x": 15.020, "y": 15.500, "z": 7.958}}, {"position": {"x": 10, "y": 17.000, "z": 5}} ]; animationStepTime: 1500' rotation="0 0 0" color="#4CC3D9"></a-box>
      
    </a-scene>

    More complex example

    Here you can find my A-Frame example with some other A-Frame components such as the last one! This example shows the reconstructed facade of the St. Margherita church of l'Aquila (Italy) with some light animation and interactive panels.