Search code examples
three.jsaframe

Use an entity's material definition within a user written A-Frame component


I have written an A-Frame component (simplified for the purpose of this question).

I would like my component to be able to receive the material information from the owning entity.

<!DOCTYPE html>
<html>
<head>
    <title>Component Test</title>
    <script src="https://aframe.io/releases/1.4.1/aframe.min.js"></script>
    <script>
        AFRAME.registerComponent('square', {
            schema: {
                size: { type: 'number' }
            },
            init: function () {

                const sizeOffset = this.data.size / 2;
                const points = [];
                points.push(new THREE.Vector2(-sizeOffset, -sizeOffset));
                points.push(new THREE.Vector2(sizeOffset, -sizeOffset));
                points.push(new THREE.Vector2(sizeOffset, sizeOffset));
                points.push(new THREE.Vector2(-sizeOffset, sizeOffset));
                var shape = new THREE.Shape(points);
                var geometry = new THREE.ShapeGeometry(shape);

                //var material = *** How do I get/inherit the component's material (to pass in to the Mesh method below)

                var mesh = new THREE.Mesh(geometry);
                this.el.object3D.add(mesh);
            },
        });

    </script>
</head>

<body>
    <a-scene>
        <a-sky color="#606060"></a-sky>
        <a-entity material="color: purple;" position="-0.5 1.6 -2" geometry="primitive: box; width: 0.2; height: 0.2;"></a-entity>
        <a-entity material="color: green;" position="0.5 1.6 -2" square="size: 0.3;"></a-entity>
    </a-scene>
</body>
</html>

But when I run this code, it displays the square in white, not green, as specified in the material definition. However, the geometry box does respect the material definition (on the left in purple).

Primitive box and UDC Square

I know that I could create a parameter for my component receiving the color, but I'd like to keep the component generic and also be able to change other properties of the material without having to add each one to my component.


Solution

  • Since the material component assigns the material to a THREE.Object acquired by .getObject3D("mesh");, you can simply set your mesh as the default mesh with
    setObject3D("mesh", mesh_object);:

    <script src="https://aframe.io/releases/1.4.1/aframe.min.js"></script>
    <script>
      AFRAME.registerComponent('square', {
        schema: {
          size: { type: 'number' }
        },
        init: function() {
          const sizeOffset = this.data.size / 2;
          const points = [];
          points.push(new THREE.Vector2(-sizeOffset, -sizeOffset));
          points.push(new THREE.Vector2(sizeOffset, -sizeOffset));
          points.push(new THREE.Vector2(sizeOffset, sizeOffset));
          points.push(new THREE.Vector2(-sizeOffset, sizeOffset));
          var shape = new THREE.Shape(points);
          var geometry = new THREE.ShapeGeometry(shape);
    
          var mesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial());
          this.el.setObject3D("mesh", mesh); // set the mesh as the default "mesh" object
        },
      });
    </script>
    
    <a-scene>
      <a-sky color="#606060"></a-sky>
      <a-entity material="color: purple;" position="-0.5 1.6 -2" geometry="primitive: box; width: 0.2; height: 0.2;"></a-entity>
      <a-entity material="color: green;" position="0.5 1.6 -2" square="size: 0.3;"></a-entity>
    </a-scene>