Search code examples
htmlcursoraframegltf

Is it possible to make only specific parts of a GLTF model clickable using AFrame?


I am loading a GLTF model in AFrame and traversing its nodes using AFrame components. I want to make only some parts of this model clickable i.e. when user hovers on these parts the cursor will be pointer and for all other parts it will be a default cursor. Is this possible using HTML or Aframe?


Solution

  • The docs are your friend! Both the cursor and the raycaster components expose the intersected object (not only the HTML element, but the underlying mesh as well).

    So You can create a custom component which will

    • check if the mouse is hovering over the model
    • keep track of the intersected mesh and apply you logic

    Check it out in this example (which i hope is extensively commented):

    <script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
    <script>
      AFRAME.registerComponent("foo", {
        init: function() {
          // check when the cursor is intersecting with this entity
          this.el.addEventListener("raycaster-intersected", evt => {
            this.intersection = evt.detail;
          });
    
          // check when the intersection is cleared
          this.el.addEventListener("raycaster-intersected-cleared", evt => {
            this.intersection = null;
          });
        },
        tick: (function() {
          // helper variable to keep track of which element is under the mouse
          var currentMesh = null;
    
          return function() {
            // if there is no intersection, return
            if (!this.intersection) {
              if (currentMesh) {
                // remove highlight if a mesh was highlighted
                this.highlight(currentMesh, false);
                currentMesh = false;
              }
              return;
            }
            // grab the current intersected object
            var intersection = this.intersection.getIntersection(this.el);
            if (!intersection) return;
            const obj = intersection.object;
            // if nothing was cached, highlight this mesh
            if (!currentMesh) {
              currentMesh = obj;
              this.highlight(currentMesh, true);
            }
            // if a mesh was highlighted, clear the old one, and highlight the new one
            if (obj.uuid !== currentMesh.uuid) {
              this.highlight(currentMesh, false);
              currentMesh = obj;
              this.highlight(currentMesh, true);
            }
          };
        })(),
        highlight: function(mesh, highlight) {
          var color = highlight ? 0xffff00 : 0x000000;
          mesh.material.emissive.setHex(color);
          mesh.material.needsUpdate = true;
        }
      });
    </script>
    </head>
    
    <body>
      <div style="z-index: 99999; position: fixed; top: 5%; text-align: center">
        <p>
          Model by <a href="https://poly.google.com/view/62zn39CRkbG">google</a>
        </p>
      </div>
      <a-scene background="color: #FAFAFA" cursor="rayOrigin: mouse">
        <a-assets>
          <a-asset-item id="model" src="https://cdn.glitch.com/0c08e4fb-3f49-4a20-bb84-a2eceb70bca4%2FWall_Art_Classical_01.gltf?v=1612251538844"></a-asset-item>
        </a-assets>
    
        <a-entity position="0 1 -3" scale="0.1 0.1 0.1" gltf-model="#model" foo></a-entity>
      </a-scene>