[SOLVED] Trouble accessing children in a glTF-imported file (load externally)

thanks

Preload when ticked means it downloads before the app starts (ie during the orange loading screen).

The app size when you export the project is not the same as the amount of data that the user downloads when viewing the app.

Take this example: https://developer.playcanvas.com/en/tutorials/load-assets-with-a-progress-bar/

All the assets have preload unticked so it only loads the engine, scripts and scene JSON file as part of the preload step (orange loading bar). This is 290KB.

This gets us into the app quickly.

When the app starts, there’s a script that requests the assets to be loaded:

// More information about streaming assets at runtime can be found here
// http://developer.playcanvas.com/en/user-manual/assets/preloading-and-streaming/

// Note: All the texture and material assets that would like to be streamed at runtime
// should have 'level' unticked and in this demo's case, have been tagged with
// 'level'
var Preloader = pc.createScript('preloader');

Preloader.attributes.add("loadingBar", {type: "entity", title: "Loading Bar"});
Preloader.attributes.add("runeCards", {type: "entity", title: "Rune Cards"});

// initialize code called once per entity
Preloader.prototype.initialize = function() {
    this.loadAssets();
};

Preloader.prototype.loadAssets = function() {
    var self = this;
    
    // Find all the assets that have been tagged 'level'
    // In this example, they are in the 'assets-to-load' folder
    var assets = this.app.assets.findByTag('level');
    var assetsLoaded = 0;
    var assestTotal = assets.length;
        
    // Callback function when an asset is loaded
    var onAssetLoad = function() {
        assetsLoaded += 1;        
        
        // Update the progress bar
        self.setLoadingBarProgress(assetsLoaded / assestTotal);        
        
        // Once we have loaded all the assets
        if (assetsLoaded === assestTotal) {
            self.onAssetsLoaded();
        }        
    };
    
    // Start loading all the assets
    for(var i = 0; i < assets.length; i++) {
        if (assets[i].resource) {
            onAssetLoad();
        } else {
            assets[i].once('load', onAssetLoad);
            this.app.assets.load(assets[i]);
        }
    }
    
    if (!assets.length) {
        this.onAssetsLoaded();
    }    
};

Preloader.prototype.onAssetsLoaded = function() {
    // Disable the loading bar so it is no longer in view
    this.loadingBar.enabled = false;
    
    // Show the runes now that all the assets are loaded
    this.runeCards.enabled = true;
};

Preloader.prototype.setLoadingBarProgress = function(progress) {
    var localScale = this.loadingBar.getLocalScale();
    localScale.x = pc.math.clamp(progress, 0, 1);
    this.loadingBar.setLocalScale(localScale);
};

And that loads the rest of the assets (which in this case is not very much) (97KB)

There’s a bit more nuance to this but that’s the general idea. This means that the developer can ensure that only the assets needed for what the user would see first (eg, a title screen with a menu) is loaded during the orange bar loading screen. And later can load/unload asset on demand depending on what the user does.

2 Likes

:slight_smile: perfect … that is already some of it (still needs to be put into a wrapped presentation … I guess - but up to you guys)

The export-standards within the Kronos-node structure seem to have been changed since/now:

Start of my gltf:

{
    "asset" : {
        "generator" : "Khronos glTF Blender I/O v1.2.75",
        "version" : "2.0"
    },
  • which might be the reason why I cannot use your (cf above also):

function printChildren(node, indentLevel) {
    var i = 0;
    var indentStr = "";
    for (i = 0; i < indentLevel; ++i) {
        indentStr += " ";
    }

    console.log(indentStr + node.name);

    for (i = 0; i < node.children.length; ++i) {
        printChildren(node.children[i], indentLevel+1);
    }
}

printChildren(pc.app.root, 0);
  • in the devtools console anymore? Leaning towards ‘pc.app.root’ as being the probable issue
    [Dave (I think) wrote about the ‘pc.’-library as being updated to something else in a repo-update?]

wait a sec - it works on a model loaded initially, but not when my/the model is loaded afterwards? How to access then >> can I do something to update the root-hierarchy, so the console can access it after the first load/run-of-scripts?

My goal is namely/now to access ‘animations’ and ‘materials’ in the gltf-structure:

if(self.entity.model){
meshes = self.entity.model.model.meshInstances; //works
glbAnims = self.entity.model.model.animations; console.log( glbAnims[0]); // does not work

PS: The 'function printChildren(node, indentLevel) { ...' only outputs the "nodes" but not "animations" nor "materials"
...

It was never meant to. It prints the node hierarchy, not it’s contents.

If you load the GLB as a container, you can access the materials and assets in the container resource: https://developer.playcanvas.com/en/api/pc.ContainerResource.html

ok, thanks … trying:

    app.assets.loadFromUrlAndFilename(recUrl, null, "container", function (err, asset) {

       try{   self.entity.addComponent('model', {
            asset: asset.resource.model
        });}catch(err0){}
        
       try{ 
            self.entity.addComponent("animation", {
       assets: asset.resource.animations,
            loop: true
        });
         var glbAnims= null;     glbAnims = self.entity.model.animations;
              
                    for (k = 0; k < glbAnims.length; ++k) {
                                         var anims = glbAnims[k];
                                  console.log( ":: ## "+anims[k]+ " @@ "+k);
                               }
          }catch(err2){}
        
         });
  • which gives me the error: Cannot read property ‘length’ of undefined

Animation resources are not on the model component, they are on the animation component:

            self.entity.addComponent("animation", {
       assets: asset.resource.animations,
            loop: true

ok, I found a solution to my problem by material-assignment in Blender (I was trying to access both ‘materials’ and ‘animations’) … And you did help :slight_smile: >> thanks

For future understanding (in case I run into similar issues):
I cannot write this without an error:


    self.entity.addComponent("animation", {
       assets: asset.resource.animations,
            loop: true
    
         var glbAnims= null;     glbAnims = self.entity.model.animations;
              
                    for (k = 0; k < glbAnims.length; ++k) {
                                         var anims = glbAnims[k];
                                  console.log( ":: ## "+anims[k]+ " @@ "+k);
                               } 
    });

PS: You dont have to use more than two minutes, as I already have a working solution - if it is at your fingertips, then great

I don’t understand the code in general. It looks like you are putting code directly into a JS object. Also, I don’t understand why you are trying to access animations from the model component? Animations are on the animation component as I previously mentioned.

Is this more like what you are trying to do?

    self.entity.addComponent("animation", {
        assets: asset.resource.animations,
        loop: true
    });

    var glbAnims = glbAnims = asset.resource.animations;

    for (k = 0; k < glbAnims.length; ++k) {
        var anim = glbAnims[k];
        console.log(":: ## " + anim + " @@ " + k);
    }

Well yes and no … I am a bit puzzled by the the component concept in general (the actual examples lies 95% of the time in forum-threads and not the API :-/ ). But again; you should not take that task upon you (I guess I will get there eventually, and overall the knowledge base is ‘quite ok’).

On the other hand, lets us say that I want to:

exchange the animation of an external glb/gltf
… then the content of the initially loaded glb comes into play - how would you replace it by code dynamically?
(for now, and with this headline, the focus and solved solution has been on surface/texture/materials)

Depends on what you want to change. For example, if you wanted to change materials on a model, you access the mesh instances and reassign the materials. Want to change the texture on a material? Get the material resource and change the appropriate texture maps.

What are you confused by?

What are you trying to do exactly? It’s probably going to be easier if there’s a concrete example for reference.