Bug?: Morph targets get lost when entity is destroyed

Hey hey :slight_smile:

We have a character in our scene that has morphTargets to animate its face. The problem is that once we change to another scene with the same character, the face can no longer be animated. After some digging I was able to find out that once the last entity that holds the render asset gets destroyed, all morph targets of the asset are cleared via this function inside the class Morph:

Steps to reproduce this issue:

  • Add 2 entities with a render component to a scene which both have the same render asset.
  • find the render asset in the console via pc.app.assets.get(assetId)
  • renderAsset.resource.meshes[0].morph.targets is an array which holds all morph targets
  • destroy one of the entities, renderAsset.resource.meshes[0].morph.targets still holds all morph targets
  • destroy the second entity, now renderAsset.resource.meshes[0].morph.targets is an empty array

How can I get the morph targets back? I tried reloading the asset via the asset registry, but that didn’t work?

Thanks

Created ticket: https://github.com/playcanvas/engine/issues/3824

I don’t have a FBX with morph targets handy to try a workaround. Just to check are you unloading the asset of the GLB container and loading it again?

Not on purpose. I am just calling this.entity.destroy() in the reproduction step, or .destroy() on the old scene root entity when switching scenes.

Do you need help with a fbx with morph targets, or can your team provide you one?

I meant in the context of trying to reload the asset :slight_smile:

If you have one kicking about that I can ‘borrow’ that would be great

Ohh, my bad :sweat_smile: No, I have not tried that, how would I do that?

I will talk to my colleagues, maybe they can provide you with it

asset.unload();
this.app.assets.load(asset);

You may want to add a callback for when the asset is loaded via asset.ready()

Hi @yaustar . I’m a colleague of @AliMoe , so if you can send me a pm I would send you the link to the model we are testing this. (Or I’ll send it to you per email).

Confirmed with the example FBX that unloading the asset and loading it again will work around the issue at the moment

3 Likes

Cool, I’m gonna try it later. Thanks :grin:

Is the ticket still relevant then? Is this the intended behaviour of morph instances?

Still relevant I think, assets should not be affected by the deletion of entities

Can you tell me what I’m doing wrong? I tried reproducing your fix in the console like this:

var asset = pc.app.assets.get(assetId)
asset.unload()
pc.app.assets.load(asset)

// Look at the morph targets
asset.resource.meshes[0].morph.targets // -> []

This didn’t load the morph targets though. What am I missing?

You will need to wait for the asset to be loaded before accessing the resource. I expect that that code gave you a not defined error.

var asset = pc.app.assets.get(assetId)
asset.unload()

asset.ready(function(asset) {
    console.log(asset.resource.meshes[0].morph.targets)
});

this.app.assets.load(asset)

https://developer.playcanvas.com/en/api/pc.Asset.html#ready

I executed each line seperately in the chrome dev tools console, so naturally there were a lot of frames between each function call. And I didn’t get a not defined error, but I was able to look at the targets array. It was empty :frowning:

I also tried it like this to trigger this process when changing scenes, but without success:

initialize() {
  this.asset = this.entity.render.asset;
  if (!this.asset.loaded) {
    this.app.assets.load(this.asset);
    this.asset.ready(function(asset) {
      console.log(asset.resource.meshes[0].morph.targets);
    });
  }

  this.on("destroy", this.onDestroy, this);
}

onDestroy() {
  this.asset.unload();
  this.asset = undefined;
}

Is the order in which i call this.app.assets.load(this.asset) and this.asset.ready() important?

I think I would need a repro to really know for sure on what’s going there. The example project I have with the egg doesn’t have an issue with morph targets not loading.

The order doesn’t matter as if the asset is already loaded, the callback in ready is called immediately.

Might be possible that the morph targets don’t get created/loaded until they are used in the scene?

Can you add me to the project in which it is working for you? Maybe I’m just missing something pretty obvious.

I’m trying to use the morph targets like this:

var meshInstance = this.entity.render.meshInstances[0];
var targetCount = meshInstance.morphInstance.morph.targets.length;

this.entity.render.meshInstances.forEach((meshInstance) => {
  for (let i = 0; i < targetCount; i++) {
    meshInstance.morphInstance.setWeight(i, weights[i]);
  }
});

There are no errors when executing this code, but targetCount is 0, so no weights will be set. Even if I set it to weights.length, the morph targets are not loaded again

@AliMoe What is your PlayCanvas username?

I don’t think there are any playcanvas frames if you’re stepping lines in the debugger … the application is paused in the debugger and does not handle loading of assets.

@yaustar My username is ALEXANDERRDX

@mvaligursky I know, but thats why I said I executed these steps in the Chrome Dev Tool Console :wink: I didn’t stop the execution of the application with the debugger

Project: https://playcanvas.com/editor/code/861745?tabs=63233430

Press 1 to destroy the entity (see right side of mouth moving)
Press 2 to create it again (notice no morph target animation)
Press 1 to destroy the entity
Press 3 to reload the asset
Wait a bit
Press 2 to create the entity (see right side of mouth moving)