Material.clone() doesn't clone all material plugins

Hello!

    console.log(line1.material.pluginManager._plugins)

    const material2 = line1.material.clone()
    console.log(material2.pluginManager._plugins)

#H1LRZ3#29

Thank you!

This PR will fix the problem:

1 Like

@Evgeni_Popov

Hello!

This causes issues for me:

because the constructor of GreasedLinePluginMaterial is

    constructor(
        material: Material,
        private _scene: Scene,
        options: GreasedLineMaterialOptions
    ) {

and the Instantiate doesn’t set any parameters, obviously.

I’ll make the constructor working even without these parameters.

OK, seems to be working now :wink:
Cloning successful!

1 Like

For ES6 to work, I think you will need to add this line at the end of the plugin file:

RegisterClass("BABYLON.GreasedLinePluginMaterial", GreasedLinePluginMaterial);

With this import:

import { RegisterClass } from "../Misc/typeStore";

Its working fine without this stuff. I’ve even created a cloning example PG and put it in the docs.

Even when using ES6? In ES6, there’s no ā€œBABYLONā€ namespace, and so ā€œBABYLON.GreasedLinePluginMaterialā€ should not be found, so Tools.Instantiate("BABYLON." + pluginClassName) should not work.

Note that it is not the cloning that will fail, but the serialization + parsing (Tools.Instantiate("BABYLON." + pluginClassName) is done in the Parse function).

1 Like

Ehm, I just tried it in the Playground, where the BABYLON namespace is available. Now I get what did you mean. I am going to have a look at it.

This was absolutely correct! I’ve looked into another classes and it’s done this way.

RegisterClass has been added to GreasedLinePluginMaterial.

Many thanks!

I’m sorry to be here again and bother you all !!!

It seems that I have also encountered the problem that the material plugin will not work after cloning, I can confirm that the source material plugin is able to work normally, the following is part of my code:

    const mesh = this.scene.getMeshByName(meshName) as AbstractMesh;
    if (!mesh) return;
    
    const originMat = mesh.material as PBRMaterial
    const cloneMat = originMat.clone(originMat.name)
    console.log(originMat.pluginManager?._plugins, 'origin');
    console.log(cloneMat.pluginManager?._plugins, 'clone');

    cloneMat.backFaceCulling = !Boolean(result)

    if (result) {
      const color = Color3.FromHexString("#b72801");
      cloneMat.alpha = 0.2;
      cloneMat.emissiveColor = color;
      mesh.material = cloneMat;
    } else {
      mesh.material = originMat;
      cloneMat.dispose()
    }

By comparing console, I found some anomalies:

the ā€˜origin’ means the source material

the ā€˜clone’ means cloned material

  1. And the cloned material’s class name changed from CarPaintPlugin to MaterialPluginBase, Although I know that CarPaintPlugin extends MaterialPluginBase
  2. cloned material _pluginDefineNames is undefined.
  3. The array of _activePlugins is missing the CarPaintPlugin I defined

Are these the root causes of the cloned material plugin not working?

@Evgeni_Popov @roland

You have to implement the serialize and parse methods for the cloning to work as expected, as well as register your plugin with the BABYLON namespace (BABYLON.RegisterClass(...)).

For eg, this PG doesn’t work when cloning the boom box material:

The boom box should be red.

Here’s the fixed PG:

Let me know if it fixes it for you.

Could you tell me what the serialize and parse methods need to do?

MaterialPluginBase also has little introduction about serialize and parse so that I can do the right thing with my plugins

Here is some of my plug-in code

export class CarPaintPlugin extends MaterialPluginBase {

static PLUGIN_NAME = 'CarPaintPlugin';

static _globalEnabled: boolean = false;

static useScanEffect: boolean = false;

static useClipEffect: boolean = false;

static useRainEffect: boolean = false;


static scanDirection: { value: number } = { value: 1 };

static steps: { value: number } = { value: -1 };
static sharedTime: { value: number } = { value: 0 };


private static _instances: Set<CarPaintPlugin> = new Set();

public iResolution: Vector3 = new Vector3(512, 512, 1.0);


constructor(material: PBRMaterial) {
    super(material, CarPaintPlugin.PLUGIN_NAME, 200, {
        USE_SCAN_EFFECT: false,
        USE_CLIP_EFFECT: false,
        USE_RAIN_EFFECT: false,
    });


    CarPaintPlugin._instances.add(this);

    this._enable(CarPaintPlugin._globalEnabled);
}


static set globalEnabled(enabled: boolean) {
    if (this._globalEnabled === enabled) return;
    this._globalEnabled = enabled;


    this._instances.forEach(instance => {
        instance._enable(this._globalEnabled);
        instance.markAllDefinesAsDirty();
    });
}

static get globalEnabled(): boolean {
    return this._globalEnabled;
}

serialize() {

}

parse(){

}


dispose() {
    CarPaintPlugin._instances.delete(this);
    CarPaintPlugin._instances.clear();
    super.dispose();
}

isCompatible(shaderLanguage: ShaderLanguage) {
}

prepareDefines(defines: MaterialDefines, _scene: Scene, _mesh: AbstractMesh): void {
    defines.USE_SCAN_EFFECT = CarPaintPlugin.useScanEffect;
    defines.USE_CLIP_EFFECT = CarPaintPlugin.useClipEffect;
    defines.USE_RAIN_EFFECT = CarPaintPlugin.useRainEffect;
}

getUniforms(_shaderLanguage?: ShaderLanguage): UniformTypes {
   return 'xxx'
}

bindForSubMesh(uniformBuffer: UniformBuffer, _scene: Scene, _engine: AbstractEngine, _subMesh: SubMesh): void {
    updateXXX()
}

static getClassName(): string {
    return 'CarPaintPlugin'
}


getCustomCode(_shaderType: string, _shaderLanguage?: ShaderLanguage): Nullable<{ [pointName: string]: string; }> {
  
    return 'xxx'
}

}

serialize should save the current state of the plugin, and parse reconfigure the plugin with the states passed in parameter.

However, as you only use static parameters, you basically don’t have anything to do, except enabling/disabling the plugin in the parse method, according to the _globalEnabled value at that moment.

If that still doesn’t work for you, I think we will need a repro in the Playground to be able to help more.

2 Likes

Ok, I will try. If the problem still cannot be solved, I will implement a copy of the playground

I’m sorry, I’m here again

After testing, using static parameters does not make the cloned material plugin work

Here is a simple playground

When line 45 is commented, The boom box is red.

That tells the story

@Evgeni_Popov

Using a static variable for ā€œglobalEnableā€ doesn’t work:

  • when you create and enable the plugin for the first material, globalEnable = true
  • when the plugin is cloned, the clone won’t be enabled because of the lines:
        if (ColorifyPluginMaterial.globalEnable === enabled) {
            return;
        }

So, you should either remove these lines or don’t make isEnabled static.

1 Like