Active Material not being set to Mesh

I am using @magavr/ecsy-babylon to interact with Babylon objects in an ECS style. One challenge has been that this library uses an old version of ecsy and Babylon , so I’ve been updating it to function with Babylon 4.2.

I am able to add ‘Mesh’ and ‘Material’ components to my scene, but the material is not being set to the Mesh, despite console.log telling me it is:

// This ECS Query runs every time a mesh material is added to an entity
this.queries.meshMaterial.added.forEach((entity: Entity) => {
      if (!this._isUrlMesh(entity)) {
        let material = entity.getMutableComponent(Material)!;
        material.object = (new BABYLON.StandardMaterial(material.color!.diffuse!, getScene(this, material.scene))); // Create the material
        this._updateMaterial(material);
        let mesh = entity.getMutableComponent(Mesh)!; // Mesh is the ecsy component, its 'object' property is the BABYLON.Mesh
        mesh.object.material = material.object;  // Set the material to the BABYLON.Mesh
        console.log(mesh.object) // This is not null, and shows as the BABYLON.Mesh.
        console.log(mesh.object.material) // The attached material. This is not null, and shows as the material created above
      }
    });

When I run my app, the mesh does not have the material specified, and selecting the mesh on the inspector shows:
sample

The ‘Active Material’ dropdown shows “None (Default Fallback)”. The material I created using the script above (#FF0000) does show in the material list, and manually selecting it from the dropdown does set the material to the mesh as expected. It also causes the “Link to Material” row to appear on the Inspector.

This feels like I am missing something simple here… Please let me know if anyone has any ideas!

1 Like

It never fails – the moment I ask for advice online I find the bug! :sweat_smile:

Posting this here for reference for others possibly using ecsy-babylon:

The ‘MeshSystem’ handles any additions, changes, or removals of mesh. The problem was the ‘changed’ query:

// This function runs every time a Mesh Component is changed (For example, by having a texture applied)
this.queries.mesh.changed.forEach((entity: Entity) => {
      disposeObject(entity.getMutableComponent(Mesh)!); // This deletes the component!
      this._updateMesh(entity, entity.getMutableComponent(Mesh)!); 
    });


// This creates an entirely new BABYLON.Mesh object
private _updateMesh(entity: Entity, mesh: Mesh) {
    switch (mesh.type) {
      case MeshTypes.Url:
        mesh.url && BABYLON.SceneLoader.IsPluginForExtensionAvailable(this._fileExt(mesh.url)) ?
          this._loadUrlMesh(entity, mesh, mesh.url) :
          this._unsupportedMesh(entity, mesh);
        break;
      default:
        mesh.object = (BABYLON.MeshBuilder as any)[`Create${mesh.type!}`].call(this, mesh.type!, mesh.options ? mesh.options : {}, getScene(this, mesh.scene));
        this._updateMeshValue(mesh);
        updateObjectsTransform(entity);
        break;
    }
  }

So every time a mesh was changed (like by adding a texture), ecsy-babylon would delete that mesh and create a brand new one, without the texture applied. Oops!

3 Likes