How to switch models using the Asset Registry without changing Materials?


#1

I’m looking for some insight into an issue I’m having with using the Asset Registry to switch between two different models. In short, the problem is that the materials in the original model get transferred to the materials in the new model.
The process I’m using is the same as shown in this tutorial with the main difference being that I’m only using the “UpdateAsset” script to create the attribute. I’m triggering the change of model from a different script.

https://developer.playcanvas.com/en/tutorials/using-assets/

This defines the attribute:

var UpdateAsset = pc.createScript('updateAsset');
UpdateAsset.attributes.add('NewTank', {
    type: 'asset',
    assetType: 'model'                       
});
// initialize code called once per entity
UpdateAsset.prototype.initialize = function() {
};
// update code called every frame
UpdateAsset.prototype.update = function(dt) {
};

This is the line I’m using to trigger the model change - and the model does change without any error messages.

this.Old_Tank1.model.model= this.Old_Tank1.script.updateAsset.NewTank.resource;

So I tried copying the materials from the new model (NewTank) to this.Old_Tank1 after running the code above, but the materials listed under NewTank show (via console.log()) that they are now the materials from Old_Tank1.

So I then figured that I’d copy the materials from NewTank to Old_Tank1 before I switch models. I can do that successfully and demonstrated that the Old_Tank1 was updated with the NewTank material by running a script that does just that. I also confirmed that I could update the materials of all the meshInstances of the Old_Tank1 to be all the same arbitrary material that I pull from the project’s assets. An all red material, for instance. So I know that I’m accessing the meshInstance materials of both models appropriately. But as soon as I replace Old_Tank1 with NewTank, the Old_Tank1 materials reappear.

So two questions.

  1. Am I simply doing something wrong with the way I’m updating the model?
  2. Is there a way to get the materials from NewTank to Old_Tank1 after I’ve updated the model?

I know that I can brute force the materials by loading the materials one by one from the app assets. But that seems like it shouldn’t be necessary. It seems like I must be missing something fundamental since it makes no sense to me that a model change would only change the geometry and not the materials associated to that model.

Thanks.


#2

The cleanest solution I’ve come up with is to read the NewTank material names into an array and then use that array to re-assign the materials after the model has been reloaded. As a fledgling coder, that may take a bit, but it seems to be a clear path and one that is semi-automatic for subsequent objects that may have a different number of meshInstances.


#3

OK. That worked. This is the code:

        for (this.miStep = 0; this.miStep < this.Old_Tank1.script.updateAsset.NewTank.resource.meshInstances.length; ++this.miStep){

                this.Old_Tank1.script.updateAsset.origMaterial[this.miStep] = this.Old_Tank1.script.updateAsset.NewTank.resource.meshInstances[this.miStep].material.name;
                //console.log(this.Old_Tank1.script.updateAsset.NewTank.resource.meshInstances[this.miStep].material.name); 
                //console.log(this.Old_Tank1.script.updateAsset.origMaterial[this.miStep]);
         }
        
        // Change the old model to the new one
        this.Old_Tank1.model.model= this.Old_Tank1.script.updateAsset.NewTank.resource;
        
        // Reset the counter
        this.miStep = 0;
        console.log(this.miStep);
        
        //Find the material assets and then assign those resources to the appropriate meshInstance     
        for(this.miStep = 0; this.miStep < this.Old_Tank1.script.updateAsset.NewTank.resource.meshInstances.length; ++this.miStep){
                this.updateMaterial = this.app.assets.find(this.Old_Tank1.script.updateAsset.origMaterial[this.miStep]);
                this.Old_Tank1.model.meshInstances[this.miStep].material = this.updateMaterial.resource;
        }

I guess it isn’t complicated really, but I find it very counter-intuitive that the new model’s materials don’t come along with the new model.

I’ll keep this topic unmarked as SOLVED for the next 12-24 hours in case anybody wants to offer some insight into the “whys” on this topic. I’ll mark it as SOLVED tomorrow.


#4

Do you have any custom material mappings on the Old_Tank1?


#5

I’m not sure what you mean by “custom” material mappings. I’m simply dropping materials onto the meshInstances that came over with the FBX import. I am not using any custom shaders or anything exotic. Most surfaces aren’t even UV mapped. Though about 7 or so are.

BTW, when I change back to Old_Tank1, its materials come back just fine without my having to do anything else.

One thing that is different from the example/tutorial app is that Old_Tank1 is an Entity with a model type asset. NewTank identified by the attributes.add function. In the example/tutorial, all of the models are introduced via that attributes.add function. I’m not sure if this would make a difference or not.


#6

Can you create a small public project that has this problem please?


#7

OK. I modified one of my sandbox projects so that I do a similar model change. (Sorry, I did not clean out much extraneous stuff) I used the same updateAsset.js script to define the model attributes as I used in my live project. I only changed the attribute names. The problem shows up the same.

Upon load you’ll see an odd scene. There will be two gray boxes - one with bumps, a colored cube in the upper left, and a colored ball in the foreground. Ignore the weird pseudo-helicopter.

ColorBall is the object that will get converted to a cube.
updateAsset.js is attached to ColorBall and provides the linkage for the ColorCube
pulse.js is where the model change is triggered.

Clicking on the right gray box switches the foreground object to be the same colored cube you see in the upper left. Clicking the left gray box with bumps switches back to the colored cube.(pulse.js)

Left-click and drag will let you rotate the active elements about their axis. I left that in because it lets you inspect the surfaces a bit better.

As you can see, the cube adopts the same four materials that were assigned to the meshInstances of the ball.

One of these two links should get you to the project:

https://playcanvas.com/editor/project/638190
https://playcanvas.com/editor/scene/804160


#8

I’m not sure if the example I posted was useful or not, but I’ll probably be clearing it out of my workspace in the next couple of days given that I have a workable solution.

So this isn’t really even an active problem as much as it is a curiosity. You’d think that a model’s materials would stay associated.


#9

Sorry, I had a quick look at it but couldn’t find a clear answer. My guess was that a material mapping was created at some stage on the model component or something was cached under the hood but couldn’t prove it :confused:

I’ve got a fork so if I do find some more time, I take another look at it.


#10

Yes, the way it seems to act is that when you associate a model to an entity all of the meshInstances are mapped and then associated to surfaces. Given that the switch seems to only affect geometry, it makes me wonder what happens if the two models have different numbers of meshInstances. Is the new model limited by the number of meshInstance in the original model? I can’t fiddle with it now, but I may do some experiments with that later.
Thanks.


#11

OK. Updated my model change project so that the ColorCube has 8 meshInstances with unique surfaces while the beginning ColorBall has only 4. If I make the change without using my material name copy and update routine, four of the surfaces on the 8 surface just show up as white. But if I run that material name copy and update routine, all eight surfaces get updated just fine. So it seems that the number of meshInstances in the initial model has no affect on the number of meshInstances you can have on a subsequent replacement model.

https://playcanvas.com/editor/project/638190
https://playcanvas.com/editor/scene/804160