Fading multiple Meshes using the tween library

Hi, I know this has been covered before, but I can’t get any of the examples to work. I have a bunch of meshes and I’m trying to smoothly fade out all but one (the parent of the entity the script is attached to). My code here complains that I shouldn’t used a function within a loop. I’m not sure how to do this. Any advice gratefully received . :slight_smile: Thank You!

//fade out layers that are not used
Hotspot.prototype.fadeOutUnusedLayers = function () 
{
    console.log("FADING LAYERS");
    aplha=1;
    for(x=0;x<this.explode_layers.length > 0;x++)
        { 
            //only fade out OTHER layers, not this one
            if(this.explode_layers[x]!=this.entity.parent)
                {
                        var data = {
                            opacity: 1
                        };
                        var material = this.explode_layers[x].model.material;

                        this.app
                        .tween(data)
                        .to({opacity: 0.0}, 1.0, pc.Linear)
                        .on('update', function () {

                            // alpha is called opacity in Playcanvas
                            material.opacity = data.opacity;
                            material.update();
                        })
                        .start();
                }
            
        }
};

I even noticed that without the tween library the following (trying to access the opacity directly) line does nothing:

this.explode_layers[x].model.material.opacity=0.5;

Am I supposed to set the material opacity to alpha in the editor or something and then directly access that?

Hi @Grimmy, there is a number of things that may be wrong here, but it’s not easy to see from your code.

Are you able to share a sample repro project to take a look?

I imagine you did see this project, right?

https://developer.playcanvas.com/en/tutorials/fading-objects-in-and-out/

Hi, yes I saw that project and I’ve tried to replicate it using just a basic switch to change the opacity to 0.5 but nothing happens (no errors etc). All the materials have blend mode alpha set.Here is the code:

//fade out layers that are not used
Hotspot.prototype.fadeOutUnusedLayers = function () 
{
    console.log("FADING LAYERS");
    aplha=1;
    this.explode_layers=this.app.root.findByTag("explode_layer");
    for(x=0;x<this.explode_layers.length > 0;x++)
        { 
            //only fade out OTHER layers, not this one
            if(this.explode_layers[x]!=this.entity.parent)
                {
                    var meshInstances = this.explode_layers[x].model.meshInstances; 
                    for(var i = 0; i < meshInstances.length; ++i) 
                    {
                        meshInstances[i].setParameter("material_opacity", 0.5);
                    }
                    
                    console.log("FADING LAYER " +x);
                }
            
        }
};

I shared the project (PlayCanvas 3D HTML5 Game Engine) and you can see this code inside hotspot.js

To replicate just choose the Purple 4 Hybrid mattress> click expand than click hotspot 3. At this point the other layers should fade to 0.5.

So playing in the console, this works:

Can you try setting the initial opacity of your materials in something different than 1.0? Much like the example project does, that will force the engine to include the opacity shader chunk, in case that missing is the issue.

image

Edit: removed part of the photo to not show the product, in case that’s not allowed for you.

Ah yes, that seems to do something. Is that not an engine bug then?

I will now see if I can get the actual fading over time working but I’m pretty confident that this was the issue.

Thanks!

Well, actually it’s a feature :innocent: Agreed though it can be confusing.

The engine tries to compile the most performant shader so if a material channel uses the default values, it may choose to omit a shader chunk.

Thanks. Well, I’ve been trying this but I still cant get anything at all to work using the tween library. I want to use the tween library because its supposedly easier to manage(!?)
Here is what I have, but nothing happens…

I cant believe its taken me all day on this and Im still no closer to fading out a model. I’d hoped this would be a simple one liner like myEntity.fadeAlpha(1.0,0,1.0). It seems like a massive waste of energy.

Hotspot.prototype.fadeOutUnusedLayers = function () 
{
    console.log("FADING LAYERS");
    this.explode_layers=this.app.root.findByTag("explode_layer");
    //aplha=1;
    for(x=0;x<this.explode_layers.length > 0;x++)
        { 
            //only fade out OTHER layers, not this one
            if(this.explode_layers[x]!=this.entity.parent)
                {
                        var data = {
                            opacity: 1
                        };
                        var material = this.explode_layers[x].model.material;

                       this.doFadeOut(data,material);
                }
            
        }
};

Hotspot.prototype.doFadeOut = function (data,material) 
{
     this.app
                        .tween(data)
                        .to({opacity: 0.0}, 1.0, pc.Linear)
                        .on('update', function () {

                            // alpha is called opacity in Playcanvas
                            material.opacity = data.opacity;
                            material.update();
                        })
                        .start();
};

Looks right, though I can’t test right now. I’ll try later to update the standard fade in out example to use a tween.

In the meantime I would try to debug the data.opacity value inside the tween update method. Try and see if the issue is with the tween or the material.

Thanks, I noticed that additionally when I set the alpha it doesn’t affect the existing shadows of the model (I would expect them to share the same alpha value). Do I need to do this separately somehow?

To get alpha clipped shadows you need to set the blending mode to None, add an opacity map and use alpha testing:

image

But if you are looking to get shadows that fade together with the model that isn’t supported.

So there is no way to fade out a model and its shadow at the same time?

No, but there may be a trick for that (I haven’t tried it myself):

Here is an example of fading in/out models using the pc.Tween library:

https://playcanvas.com/editor/scene/1197841

3 Likes

Is this a non-moving model? If so, I’ve seen some apps use a plane with a texture of the shadow that they can fade to get around this issue.

2 Likes

Of course, this is fairly trivial when the object has only one material. I’ve always thought it odd that PlayCanvas doesn’t have an object level dissolve as is common in many 3D rendering applications.

I ended up writing a script to serve that function. But my 3D animation experience still leaves me thinking this should be an application level function.

1 Like

That’s an interesting idea. I may have to give that a try.
Thanks.

So the tween project I’ve posted will work with no change for models with multiple materials, since it loops through the available mesh instances.

Though you will still have to prepare each material to include the opacity shader chunk. On that subject agreed, that could be simplified. Try posting a feature request about it in the engine repo.

1 Like

Ah - I just ran the project and didn’t look closely at the script.

Yes, you need to deal not only with making sure the materials have the opacity shader chunk, but you need to consider what the original material opacity was. If your object has windows at 60% opacity, fully fading in the object would mean restoring the window to 60% opacity, not 100%.

The following is (I think) the current version of my objectFade.js. You apply it to the entity and then typically would manipulate the object’s opacity via a tween.

//// Version 4.0
//// Provides a way to set opacity for all materials on a specific object independly of other objects using a single variable.
//// Children must have a different name than the name of the main parent entity that this script is attached to.
//// Usage: Add this script to an entity that has a model component.
//// Initial Opacity is the object opacity that will be used when the object is first loaded.
//// Object Opacity is the opacity of the object after the initial load. This can be changed either in Editor of by using a script (such as with a tween).
//// Both of the above two values are values that are multiplied by the object meshInstance's material opacity value. 
//// This allows the script to proportionally fade and restore surfaces that start with partial transparency.
//// Example tween:
//        this.tween = this.someEntity.tween(this.someEntity.script.objectFade).to({opacity: 0}, 1, pc.SineInOut)
//       //.yoyo(true).repeat(10) // unremark to fade up and down five times
//       .delay(0);
//       this.tween.start();
//// Directly set opacity like this:
//        this.someentity.script.objectFade.opacity = 0.75;

var objectFade = pc.createScript('objectFade');

    objectFade.attributes.add('initialOpacity', {
        title: 'Initial Opacity',
        type: 'number',
        default: 1   
    });

    objectFade.attributes.add('opacity', {
        title: 'Object Opacity',
        type: 'number',
        default: 1   
    });
// Determines if the child objects will inherit the opacity setting or not.
    objectFade.attributes.add('inherit', {
        title: 'Inherit Opacity',
        type: 'boolean',
        default: true   
    });
// Forces all materials to have the "Alpha to Coverage" setting as true.  This will apply to all children objects/materials if "Inherit Opacity" is true.
    objectFade.attributes.add('alpha2cover', {
        title: 'Alpha to Coverage',
        type: 'boolean',
        default: true   
    });
                              
    // initialize code called once per entity
    objectFade.prototype.initialize = function() {
		//this.initialOpacity = 0.999;
		this.previousOpacity = undefined;
        this.opacityFactor = undefined; 
        this.miStep = 0;
        this.chiStep = 0;
        this.toggle = false;
        this.rootName = undefined;
        

};

    objectFade.prototype.objectFade = function () {

};

    // update code called every frame
    objectFade.prototype.update = function(dt) {
        // Ensures that all the meshInstance materials have some transparency and a blendType
        if(this.entity.model.meshInstances[0].parameters.material_opacity === undefined){
 			for (this.miStep = 0; this.miStep < this.entity.model.meshInstances.length; ++this.miStep){
                this.opacityFactor =this.entity.model.meshInstances[this.miStep].material.opacity;
				this.entity.model.meshInstances[this.miStep].material.opacity = (this.opacityFactor * 0.999999);
				this.entity.model.meshInstances[this.miStep].material.blendType = pc.BLEND_NORMAL;
				this.entity.model.meshInstances[this.miStep].material.update();	
				this.previousOpacity = this.initialOpacity;
                this.opacity = this.initialOpacity;
				this.entity.model.meshInstances[this.miStep].setParameter('material_opacity', (this.opacity * this.opacityFactor));
                
			}
            if(this.inherit === true){
                this.entity.forEach(function (node) {
                    if(node.model && node.name !== this.entity.name){
                        //console.log('all children', node.name);

                        var meshInstances = node.model.meshInstances;
                    for (this.miStep = 0; this.miStep < meshInstances.length; this.miStep++){
                        var material = meshInstances[this.miStep].material;
                        if( ! material) continue;
                        //console.log('material', material.name);

                        this.opacityFactor = material.opacity;
                        material.opacity = (this.opacityFactor * 0.999999);
                        if(this.alpha2cover === true){
                            material.alphaToCoverage = true;   
                        }
                        material.blendType = pc.BLEND_NORMAL;
                        material.update();	
                        this.opacity = this.initialOpacity;
                        node.model.meshInstances[this.miStep].setParameter('material_opacity', (this.opacity * this.opacityFactor));
                    }
                    }
                                   
                }, this);

            }
        }
    
		if(this.previousOpacity === this.opacity){
			//do nothing
		}else{
			this.miStep = 0;
			for (this.miStep = 0; this.miStep < this.entity.model.meshInstances.length; ++this.miStep){
                    this.opacityFactor = this.entity.model.meshInstances[this.miStep].material.opacity;
                    this.entity.model.meshInstances[this.miStep].setParameter('material_opacity', (this.opacity * this.opacityFactor));
		}
            
            if (this.inherit === true){
                 this.entity.forEach(function (node2) {
                    if(node2.model && node2.name !== this.entity.name){
                        //console.log('all children', node2.name);

                        var meshInstances2 = node2.model.meshInstances;
                        for (this.miStep = 0; this.miStep < meshInstances2.length; this.miStep++){
                            var material2 = meshInstances2[this.miStep].material;
                            if( ! material2) continue;
                            //console.log('material2', material2.name);
                            this.opacityFactor = material2.opacity;                   
                            node2.model.meshInstances[this.miStep].setParameter('material_opacity', (this.opacity * this.opacityFactor));

                        }
                    }
             
            }, this);
        }
    }
        this.previousOpacity = this.opacity;
        
};
2 Likes