Creating materials in the PC Editor


#1

I’m using a JSON file to create materials within the editor. The files contains about 17 materials so I loop through each one and use the editor hooks to create it.

My problem is that although 17 materials are created, changing the material’s name and adding a texture only works once.

The code should explain it better, but firing the event ‘assets:add’ with editor.once() changes the first material’s name/diffuseMap, and using editor.on() changes all of them but to the same values.

const createMaterial = (folderName, textureName, materialName) => {
	const folder = findAssetType(folderName, 'folder');
	const texture = findAssetType(textureName, 'texture');

	editor.call('assets:create:material', { parent: folder[1] });
	editor.once('assets:add', asset => {
	    asset.set('name', materialName);
	    asset.set('data.diffuseMap', texture[1].get('id'));
	});
}

What I’m looking for is to either get the material that’s created by editor.call('assets:create:material', { parent: folder[1] }); or a way to wait until the event has fired and executed. Are there Promises in here at all?

Thanks,

Mei


#2

In the end I created two separate buttons within the UI (I’m building a Chrome plugin). One creates a load of New Materials, the other applies textures to those materials. Not perfect but good enough for our needs.

Things I tried was forcing a hard wait, a while loop for searching for the new asset, moving the editor.once() call to before the call to create the asset, and creating a load of new materials before immediately applying textures.

None of these worked. It looks like there needs to be a stop in JS execution so that the New Materials can be found by editor.call('assets:findOne', a => a.get('type') == 'material' && a.get('name') == 'New Material');.


#3

Another update on this. I managed to crack it in the end.

Instead of creating a New Material, then applying a texture, I do it all before creating the material:

// taken from editor.js, line 39,278
const createMaterialApplyTexture = (materialName, textureId, parent, colours, tiling) => {
    var asset = {
        name: materialName,
        type: 'material',
        source: false,
        preload: true,
        data: editor.call('schema:material:getDefaultData'),
        parent: parent,
        scope: {
            type: 'project',
            id: config.project.id
        }
    };

    // overwrite the defaults (provided by editor.call('schema:material:getDefaultData'))
    // with the texture, tiling and colour values
    asset.data.diffuseMap = textureId;
    asset.data.diffuseMapTint = true;
    asset.data.diffuse = [colours.r, colours.g, colours.b];
    asset.data.diffuseMapTiling = [tiling.x, tiling.y];

    editor.call('assets:create', asset);
}

This method does away with two separate buttons to create the materials then apply the textures. I was using editor.call('assets:create:material'), but using the above is much easier.

I have over 50 projects to create, each with 50 - 200 entities and associated materials to create. All the data are in JSON files. This function saves us days in setup time.