scene.freezeActiveMeshes() does not call the right code

The function in question, calls _freeze() on each mesh

public freezeActiveMeshes(skipEvaluateActiveMeshes = false): Scene {
    this.executeWhenReady(() => {
        if (!this.activeCamera) {
            return;
        }

        if (!this._frustumPlanes) {
            this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix());
        }

        this._evaluateActiveMeshes();
        this._activeMeshesFrozen = true;
        this._skipEvaluateActiveMeshesCompletely = skipEvaluateActiveMeshes;

        for (var index = 0; index < this._activeMeshes.length; index++) {
            this._activeMeshes.data[index]._freeze();
        }
    });
    return this;
}

At the AbstractMesh level, _freeze() does not do anything:

 public _freeze() {
    // Do nothing
}

It is over ridden in Mesh, but it does not really do anything, but set some values for instances. I think this has something to do with materials??

public _freeze() {
    if (!this.subMeshes) {
         return;
    }

    // Prepare batches
    for (var index = 0; index < this.subMeshes.length; index++) {
        this._getInstancesRenderList(index);
    }

    this._effectiveMaterial = null;
    this._instanceDataStorage.isFrozen = true;
}

It should call freezeWorldMatrix() on TransformNode:

 public freezeWorldMatrix(newWorldMatrix: Nullable<Matrix> = null): TransformNode {
    if (newWorldMatrix) {
        this._worldMatrix = newWorldMatrix;
    } else {
        this._isWorldMatrixFrozen = false;  // no guarantee world is not already frozen, switch off temporarily
        this.computeWorldMatrix(true);
    }
    this._isDirty = false;
    this._isWorldMatrixFrozen = true;
    return this;
}

The test I used to cause me to go through this is that after freezeActiveMeshes(), after scene.render() has run at least once, is myMesh.isWorldMatrixFrozen still returns false.

Hi @JCPalmer
I think there’s been a misunderstanding! :slight_smile:
scene.freezeActiveMeshes() will stop the evaluation of which meshes are “active” /in frustum (camera view) and thus which meshes should be included when doing rendering calculations, etc.

mesh.freezeWorldMatrix() is the correct function if you wish to freeze the world matrix of a mesh.

something like this should do the job for all meshes:

scene.meshes.forEach(m => { m.freezeWorldMatrix() } );
2 Likes

To add to @aWeirdo, nothing stop you to update mesh position/translation/scale even when the active mesh list has been frozen.

Freezing the creation of the active mesh list and freezing the mesh world matrices are two separate process.

Ok, I can freeze all myself, which is sort of ironic in that the scene I am just starting actual assembly of, has a massive amount of animation. Since I have a formal animation sub-system, I know just what frames & which meshes need to be computed below application level. Also helps that I supporting root bone translation / rotation, so meshes with armatures never move / rotate.

While 95% of the meshes will always evaluate as active, some will not. Will just set those to alwaysSelectAsActiveMesh to avoid a wasteful isInFrustum check.

So what does, Mesh._freeze() accomplish?

In addition to “What does the undocumented mesh._freeze() / mesh._freeze() do?” to close this topic out, just a note for anyone else trying to manage all worldmatrix computation themselves.

Use freezeWorldMatrix() to accomplish a one time calc, not computeWorldMatrix(true). Reason is in the first few lines:

public computeWorldMatrix(force?: boolean): Matrix {
    if (this._isWorldMatrixFrozen && !this._isDirty) {
        return this._worldMatrix;
    }
    . . .
}

Even though you indicated true for force, it is not consulted for some reason till later (don’t think I did that). You need to rely on _isDirty being set to true. I was setting via LerpToRef, and this did not “dirty it”. You can also manually set, but if using typescript & not in a mesh subclass, you must set _isDirty indirectly.

I wrote the part of freezeWorldMatrix() which does the one time “final” set. It temporarily turns off the frozen switch inside. Cleaner than doin it yourself.

Also, what about the kids? This is my final code (with the less desirable way in comments):

if ((this._doingRotation || this._doingMovePOV) && this._isMesh && (<BABYLON.AbstractMesh> this._node).isWorldMatrixFrozen) {
    console.log('computing matrix');
//    (<BABYLON.AbstractMesh> this._node)["_isDirty"] = true;
//    (<BABYLON.AbstractMesh> this._node).computeWorldMatrix(true);
    (<BABYLON.AbstractMesh> this._node).freezeWorldMatrix();

    for (const kid of this._node.getChildMeshes() ) {
        if (kid.isWorldMatrixFrozen || TimelineControl.computeWrldMatSkipped) {
//            kid["_isDirty"] = true;
//            kid.computeWorldMatrix(true);
            kid.freezeWorldMatrix();
        }
    }
}
1 Like