Jump to content

How to create/add assets directly to the AssetContainer ?


HoloLite
 Share

Recommended Posts

It seems to me that there is no direct way to create asset (light, camera, meshes etc) and add it into the asset container.

What I am trying to do is the create the assets programmatically (not using load scene) into an container without polluting the scene object itself.

The typical code fragment to create/add an asset into a container looks like the following:

 

scene: BABYLON.Scene;
assets: BABYLON.AssetContainer;

...

let camera = new BABYLON.FreeCamera(... , scene);
scene.removeCamera(camera);
assets.cameras.push(camera);

Basically it has to be done in 3 steps:

1. create/add the asset into scene (all existing api to create mesh takes the scene object)

2. remove asset from scene

3. add the asset to container

This is true for all other asset types like light, meshes, animations etc.

Is there an easier to do this (like doing in 1 step?) Please advise. Thanks

Link to comment
Share on other sites

I wonder if the following approach will work (not ideal, but a bit more manageable)

1. Create all the needed assets (meshes, cameras, lights) as usual into the scene object.

2. Enumerate all asset array properties of the scene object like scene.camera, scene.lights, scene.animations, etc) and manually copy them into the corresponding array properties in the asset container object. (Question: does the asset container has all of the asset array properties of the scene object ? if not this approach won't work)

3. call assetContainer.removeAllFromScene()

I guess, this will cleanly 'move' all the assets from the scene object to the container object.  Any comments ?

Link to comment
Share on other sites

Yea, your approach seems reasonable. Currently all manual asset creation adds the asset to the scene. Previously there was some discussion about possibly making AssetContainer and scene inherit from a same base interface so either could be passed into an asset creating method but this was not implemented due to the somewhat unnatural complexity of the change.

Question: does the asset container has all of the asset array properties of the scene object 

Yes, it should contain all the asset arrays the scene has.

For now I would recommend manually adding your assets to the AssetContainer and then calling removeAllFromScene like you mentioned. See this http://www.babylonjs-playground.com/#5NFRVE#1

Link to comment
Share on other sites

@trevordev thanks for the input.

In that case, then I think we should add into 3.2 release the helper method called something like AssetContainer.moveAllFromScene which does the steps I described. I think many users will benefit from this.

In retrospect, perhaps the better approach is not to expose the AssetContainer at all. 

The root issue is that the current BABYLON.Scene and its vr helper object can only be used as singleton (even if you intend to create multiple scenes), otherwise you will see rendering problem inside the VR headset.

A high-level abstraction of Scene concept that internally manages its assets as user switches scenes would be a cleaner approach, imo.

Another approach is not to make vr helper associated with scene but rather make it explicitly global entity. 

 

Link to comment
Share on other sites

Adding an AssetContainer.moveAllFromScene method sounds possible. Could you expand more on your exact use case? After you create your assets in code, how are you planning to use them? Is your code already written with multiple scenes?

Beyond the vrHelper issue you mentioned, the assetContainer can also be used to load assets to be used at a later time without being automatically added to the scene.

It does seem like the AssetContainer will result in code that has been written with multiple scenes to need to be rewritten to use a single scene and multiple AssetContainers which isn't very obvious. Having the helper method you suggested could be good for alleviating that pain.

Switching assets between scenes is rather complex due to how tightly coupled each asset is to the scene when created. Adding support for that would likely be good but the change would be large and difficult to avoid breakage. Having special cases for certain objects like the vrHelper to move between scenes automatically might also cause confusion to users not expecting that behavior.

If you create a github issue for the moveAllFromScene I can take a look at implementing it when free.

Link to comment
Share on other sites

My codes are all in typescript as I know little javascript. Unfortunately the playground takes only javascript.

That said though, @brianzinn has wrote nice sample code: http://playground.babylonjs.com/#JA1ND3#48  that illustrates similar situation.

Notice how he created only a single object of scene and vr object and iterates each scene container and loads them into the scene object.

If you are using VR headset, this is the only way to switch scenes afaik. Doing otherwise will result in the scene rendered incorrectly in the vr headset.

What I am trying to do is similar to this except that I don't use screen loader, I programmatically create the assets.

I agreed with your statements that switching scene is a complex op, that's why I'd rather avoid using AssetContainer, but currently this is the only options I can think of.

So far the approach I describe above (to juggle the assets using the containers) seem to work. But there is this weird thing about how the .cameras properties not showing up in the scene - I am still trying to figure out the root cause.

Link to comment
Share on other sites

I had to do both. Implementation of moveAllFromScene is the following. I prefer that this is implemented by the framework in the near future so that any internal changes in the scene object like addition or removal of assets properties will be taken care of as well.

import 'babylonjs'

export class AssetContainerEx extends BABYLON.AssetContainer {
    constructor(scene: BABYLON.Scene) {
        super(scene);
    }

    moveAllFromScene(): void {
        Array.prototype.push.apply(this.actionManagers, this.scene._actionManagers);
        Array.prototype.push.apply(this.animations, this.scene.animations);
        Array.prototype.push.apply(this.cameras, this.scene.cameras);
        Array.prototype.push.apply(this.geometries, this.scene.getGeometries());
        Array.prototype.push.apply(this.lensFlareSystems, this.scene.lensFlareSystems);
        Array.prototype.push.apply(this.lights, this.scene.lights);
        Array.prototype.push.apply(this.materials, this.scene.materials);
        Array.prototype.push.apply(this.meshes, this.scene.meshes);
        Array.prototype.push.apply(this.morphTargetManagers, this.scene.morphTargetManagers);
        Array.prototype.push.apply(this.multiMaterials, this.scene.multiMaterials);
        Array.prototype.push.apply(this.skeletons, this.scene.skeletons);
        Array.prototype.push.apply(this.particleSystems, this.scene.particleSystems);
        Array.prototype.push.apply(this.sounds, this.scene.mainSoundTrack.soundCollection);
        Array.prototype.push.apply(this.transformNodes, this.scene.transformNodes);

        this.removeAllFromScene();
    }
}

 

Link to comment
Share on other sites

@trevordev

I figured out the issues with the missing camera stuff. Basically the call to scene.createDefaultVRExperience() will create several cameras (5 of them now) which are used for VR purpose.

You do want to keep these cameras in the scene object as you shuffle assets in and out from the containers to the scene object. 

That said I have to modify the moveAllFromScene to take optional keepCameras for this very purpose.

import 'babylonjs'

export class AssetContainerEx extends BABYLON.AssetContainer {

    constructor(scene: BABYLON.Scene) {
        super(scene);
    }

    moveAllFromScene(keepCameras?: BABYLON.Camera[]): void {
        Array.prototype.push.apply(this.actionManagers, this.scene._actionManagers);
        Array.prototype.push.apply(this.animations, this.scene.animations);

        if (keepCameras === undefined) {
            keepCameras = [];
        }
        for (let camera of this.scene.cameras) {
            let moveCamera = true;
            for (let keepCamera of keepCameras) {
                if (camera === keepCamera) {
                    moveCamera = false;
                    break;
                }
            }

            if (moveCamera) {
                this.cameras.push(camera);
            }
        }

        Array.prototype.push.apply(this.geometries, this.scene.getGeometries());
        Array.prototype.push.apply(this.lensFlareSystems, this.scene.lensFlareSystems);
        Array.prototype.push.apply(this.lights, this.scene.lights);
        Array.prototype.push.apply(this.materials, this.scene.materials);
        Array.prototype.push.apply(this.meshes, this.scene.meshes);
        Array.prototype.push.apply(this.morphTargetManagers, this.scene.morphTargetManagers);
        Array.prototype.push.apply(this.multiMaterials, this.scene.multiMaterials);
        Array.prototype.push.apply(this.skeletons, this.scene.skeletons);
        Array.prototype.push.apply(this.particleSystems, this.scene.particleSystems);
        Array.prototype.push.apply(this.sounds, this.scene.mainSoundTrack.soundCollection);
        Array.prototype.push.apply(this.transformNodes, this.scene.transformNodes);

        this.removeAllFromScene();
    }
}

 

Link to comment
Share on other sites

I found more states that need to be saved. These are essentially artifacts of the VR helper during teleportation/interactions.

Now my app can switch scenes and does the teleportation and interaction within each scene correctly as I switch scenes.

The more complete codes look like the following:

import 'babylonjs'

export class KeepAssets {
    cameras: BABYLON.Camera[] = [];
    meshes: BABYLON.Mesh[] = [];
    geometries: BABYLON.Geometry[] = [];
    materials: BABYLON.Material[] = [];
}

export class AssetContainerEx extends BABYLON.AssetContainer {
    private moveAssets<T>(sourceAssets: T[], targetAssets: T[], keepAssets: T[]): void {
        for (let asset of sourceAssets) {
            let move = true;
            for (let keepAsset of keepAssets) {
                if (asset === keepAsset) {
                    move = false;
                    break;
                }
            }

            if (move) {
                targetAssets.push(asset);
            }
        }
    }

    constructor(scene: BABYLON.Scene) {
        super(scene);
    }

    moveAllFromScene(keepAssets?: KeepAssets): void {

        if (keepAssets === undefined) {
            keepAssets = new KeepAssets();
        }

        this.moveAssets(this.scene.cameras, this.cameras, keepAssets.cameras);
        this.moveAssets(this.scene.meshes, this.meshes, keepAssets.meshes);
        this.moveAssets(this.scene.getGeometries(), this.geometries, keepAssets.geometries);
        this.moveAssets(this.scene.materials, this.materials, keepAssets.materials);

        Array.prototype.push.apply(this.actionManagers, this.scene._actionManagers);
        Array.prototype.push.apply(this.animations, this.scene.animations);
        Array.prototype.push.apply(this.lensFlareSystems, this.scene.lensFlareSystems);
        Array.prototype.push.apply(this.lights, this.scene.lights);
        Array.prototype.push.apply(this.morphTargetManagers, this.scene.morphTargetManagers);
        Array.prototype.push.apply(this.multiMaterials, this.scene.multiMaterials);
        Array.prototype.push.apply(this.skeletons, this.scene.skeletons);
        Array.prototype.push.apply(this.particleSystems, this.scene.particleSystems);
        Array.prototype.push.apply(this.sounds, this.scene.mainSoundTrack.soundCollection);
        Array.prototype.push.apply(this.transformNodes, this.scene.transformNodes);

        this.removeAllFromScene();
    }
}

 

Link to comment
Share on other sites

  • 2 weeks later...

@trevordev I just synced to alpha6 and see the changes you made. Thank you!

I have a suggestion for moveAllFromScene implementation: make the implementation more general to accommodate future additional keep assets.

That said, the codes would look like the following:

moveAllFromScene(keepAssets?: KeepAssets): void {

        if (keepAssets === undefined) {
            keepAssets = new KeepAssets();
        }

        this.moveAssets(this.scene.cameras, this.cameras, keepAssets.cameras);
        this.moveAssets(this.scene.meshes, this.meshes, keepAssets.meshes);
        this.moveAssets(this.scene.getGeometries(), this.geometries, keepAssets.geometries);
        this.moveAssets(this.scene.materials, this.materials, keepAssets.materials);
        this.moveAssets(this.scene._actionManagers, this.actionManagers, keepAssets.actionManagers);
        this.moveAssets(this.scene.animations, this.animations, keepAssets.animations);
        ... // do this for all assets


        this.removeAllFromScene();
    }

I noticed you already made the KeepAssets class handling all assets which is good.

This change will keep the logic intact as the caller is responsible to filling the correct keep assets prior to calling and the default assets in KeepAssets are just empty arrays.

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...