Adding animations to meshes and dispose on runtime

Hey everyone :slightly_smiling_face:

I’m trying to load and dispose skeletons&meshes on runtime working with the assetsManager.

I tried a method described by @Dad72 in the old Forum (link). Is it still a valid solution?

  1. You load your dummy model that contains the animations with ImportMesh, then you create animations skeleton.createAnimationRange (‘walk’, keyStart, KeyEnd). This dummy model can be disposed afterwards.
  1. You load the other models without animations which contains the same number of bones as the dummy model and you copy the animations there with skeleton.copyAnimationRange (skeleton, ‘walk’, true);
  1. You play the animations following the user interaction by calling the animation you need by name with skeleton.beginAnimation (’’ walk ', true, 1.0);

I can’t seem getting it to work. The ‘dummy’ meshes show up in the scene playing their animations.
I get different results in Chrome and Mozilla. While Chrome is showing just one of the meshes. Firefox is showing all of them. Im using babylon 3.3.0

My theory is that I do something wrong with disposing the loaded meshes?
Ideas/hints on that problem are highly appreciated. Thanks in advance!:pray:

Here’s how I use it in my code:

load() 
{
    let fileName = this.name;
    if(this.nameMesh != '')
    {
        fileName = this.nameMesh;
    }

    // LOADING THE ANIMATIONS
    for (let i = 0; i < this.nameAnimations.length; i++) //nameAnimations: list of animationfilenames
    {
        // building the corresponding pathnames for each animation(file)
        let nameTmp = this.name + '_' + this.nameAnimations[i] + '.babylon' 
        let taskLoad = this.assetsManager.addMeshTask(nameTmp + "Task", "", 
                                            ASSETPATH + "/anims/" + this.name + '/', nameTmp);
        
        taskLoad.animation = this.nameAnimations[i];
        taskLoad.parentObject = this;
        taskLoad.onSuccess = function () 
        {
            this.parentObject.loadAnimation(this.loadedSkeletons, this.animation);

            // We don't need the mesh of the animation file
            for (let i = 0; i < this.loadedMeshes.length; i++) 
            {
                this.loadedMeshes[i].dispose();
                this.loadedMeshes[i] = null; //

            }
            // We don't need the skeleton of the animation file  
            for (let i = 0; i < this.loadedSkeletons.length; i++) 
            {
                this.loadedSkeletons[i].dispose();
                this.loadedSkeletons[i] = null;
            }
        }
    }


    // LOADING THE MESH
    let taskLoad = this.assetsManager.addMeshTask(this.name + "Task", "", 
                                        ASSETPATH + "/meshes/", fileName + ".babylon");
    
    taskLoad.parentObject = this;
    taskLoad.onSuccess = function (task) 
    {
        this.parentObject.skeletons = this.loadedSkeletons; 
        
        this.parentObject.applyMaterial(this.loadedMeshes);
        
        for(let i=0; this.loadedMeshes.length; i++)
        {
            this.loadedMeshes[i].containedInSceneObject = this.parentObject;    // Giving all meshes a reference to their SceneObject for GazeInteractions
            this.parentObject.nameMeshes.push(this.loadedMeshes[i].id);         // and creating a list of MeshID's for SceneObject to know which Meshes it contains
            this.parentObject.containsMeshes.push(this.loadedMeshes[i]);        // and creating a list of Meshobjects
        }
        
    }
}

Found further posts on dispose here. Maybe they help others:

(Sorry for the long post. I’m starting with babylon and JS. Please beware of very stupid mistakes :wink: )

Hello and welcome!!

To help you further we are going to need a repro case in the playground (check this doc to reference external assets: Using External Assets - Babylon.js Documentation)

This will be far easier as we can then repro your issue locally :wink:

Alright, I thought maybe you see the mistake at first glance, or the method I’m trying is already obsolete. Thanks for the link @Deltakosh . Will work on a PG (It will take some time for me)

1 Like

So I created a playground and tried to strip down the problem as far as I could.
https://playground.babylonjs.com/#2HQ6RN#21

‘Luckily’ the error occurs in the playground the same way it does in my local project.
I observed three possible viewport results:

  1. Chrome: Sometimes it just works perfectly. Only one Character is shown. Playing ‘sleepingloop’ animation.
    56

  2. Chrome: Sometimes it shows an extra Character T0-Pose (The Mesh I want to copy the animations to).

  3. Firefox: Multiple Characters are shown. All playing ‘their’ animation.

While testing I noticed that there are differences when refreshing the page with clear cache and just a normal refresh. Please ignore the size differences on the character. When the animation is applied correctly like in result 1. it will have the right size.

Thanks a lot in advance if somebody dares to take a look at the newbie code! :pray:

PS.: Unfortunately I couldn’t get the delivr conversion to work. :building_construction:Still working on it

You have a race condition: there is no guarantee (depending on network speed, cache, etc…) that the animations will load BEFORE the meshes so your loadAnimation function can get called before the this.skeletons array is ready

Thanks @Deltakosh !

I got the jsdelivr link to work: https://playground.babylonjs.com/#2HQ6RN#22
(‘Checked’ the doc. Found a little typo. Corrected and created pull request.)

I thought a race condition couldn’t accur by using .onSuccess with the assetsManager, mhm.
Has anyone advise, best-practice or thinks I should look at to solve this issue?

Maybe Use Promises - Babylon.js Documentation ?

The onSuccess is called per task so I will recommend to move the code from individual task.onSuccess to overall manager.onFinish

1 Like

I think I fixed the race condition https://playground.babylonjs.com/#2HQ6RN#33

Animations are now transferred and additional meshes are disposed, after all assets have been loaded. (assetsManager.onFinish{...} )

Thank you @Deltakosh for the guidance!

1 Like

Excellent!