Add metadata on GLB export

Hi.
As the title say.
I’m haveing a mesh with some matedata properties in scene, and I export it as glb, unfortunatelly on import glb file that metadata is missing.
I read somewhere that by default is not possible to add these meta to glb, but I saw that glb/gltf has some ‘extras’ property.
My question is: On exporting as gltf/glb if exported meshes contain metadata, can I in someway to add them to gltf/glb nodes as extras or something like this?

Adding @kcoley and @bghgary to the thread.

One more question, related to this exporter.
Can we add draco compression on it or not?

We can…the question is when :slight_smile:

On exporting as gltf/glb if exported meshes contain metadata, can I in someway to add them to gltf/glb nodes as extras or something like this?

Not currently. It will be tricky to implement since Babylon objects don’t match 1:1 to glTF objects. Do you have a specific scenario?

Good question as well :smile:

Yes, unfortunatelly I think is pretty particular.
I allow the user to upload different types of meshes, like obj, stl, babylon or gltf put them together and export as new mesh.
For this new mesh I want to add few flags as meta, like, if it can be rotate in a specific axis, and more. So on import into second scene, simply check meta to know which functions are alowed.

Sounds reasonable. Can you file a feature request on GitHub?

If i can chime in here… Yes you can add metadata to your GLTF JSON in extras or your can create a real GLTF Extension and add your metadata there. @bghgary made excellent GLTF parsing hooks that you can use in your custom Babylon GLTF Importer Extension. This is the heart and soul of how the Babylon Toolkit - 2019 Edition is working. I serialize Unity data as extra metadata using my CVTOOLS_unity_metadata extension.

Then in Babylon i created a GLTF Importer Extension to properly parse all that gltf extra metadata:

this is what my extension looks like so far:

const CVTOOLS_NAME = "CVTOOLS_unity_metadata";
/**
 * Babylon canvas tools loader class
 * @class CVTOOLS_unity_metadata
 * [Specification](https://github.com/MackeyK24/glTF/tree/master/extensions/2.0/Vendor/CVTOOLS_unity_metadata)
 */
class CVTOOLS_unity_metadata implements BABYLON.GLTF2.IGLTFLoaderExtension {
    /** The name of this extension. */
    public readonly name = CVTOOLS_NAME;

    /** Defines whether this extension is enabled. */
    public enabled = true;

    private _loader: BABYLON.GLTF2.GLTFLoader;

    /** @hidden */
    constructor(loader: BABYLON.GLTF2.GLTFLoader) {
        this._loader = loader;
    }

    /** @hidden */
    public dispose() {
        delete this._loader;
    }

    /** @hidden */
    public onLoading(): void {
        //console.log("CVTOOLS - Loading Scene");
    }    

    /** @hidden */
    public onReady(): void {
        //console.log("CVTOOLS - Scene Ready");
    }    

    /** @hidden */
    public loadSceneAsync(context: string, scene: BABYLON.GLTF2.Loader.IScene): BABYLON.Nullable<Promise<void>> {
        if (scene.extras != null && (<any>scene.extras).metadata != null) {
            const metadata:any = (<any>scene.extras).metadata;
            // ..
            // console.log("CVTOOLS - Parsing Scene Properties: " + scene.name);
            // console.log(metadata);
            // ..
            // Setup Scene Clear Coloring
            // ..
            this._loader.babylonScene.autoClear = true;
            this._loader.babylonScene.ambientColor = BABYLON.Color3.Black();
            if (metadata.hasOwnProperty("clearcolor")) {
                this._loader.babylonScene.clearColor = BABYLON.Utilities.ParseColor4(metadata.clearcolor);
            }
            // ..
            // Setup Scene Fog Information
            // ..
            if (metadata.hasOwnProperty("fogmode")) {
                let fogmode:number = metadata.fogmode;
                if (fogmode > 0) {
                    this._loader.babylonScene.fogMode = fogmode;
                    this._loader.babylonScene.fogEnabled = true;
                    if (metadata.hasOwnProperty("fogdensity")) { 
                        this._loader.babylonScene.fogDensity = metadata.fogdensity;
                    }
                    if (metadata.hasOwnProperty("fogstart")) { 
                        this._loader.babylonScene.fogStart = metadata.fogstart;
                    }
                    if (metadata.hasOwnProperty("fogend")) { 
                        this._loader.babylonScene.fogEnd = metadata.fogend;
                    }
                    if (metadata.hasOwnProperty("fogcolor")) { 
                        this._loader.babylonScene.fogColor = BABYLON.Utilities.ParseColor3(metadata.fogcolor);
                    }
                }
            }
            // ..
            // Setup Scene Ambient Lighting
            // ..
            if (metadata.hasOwnProperty("ambientlighting")) {
                let ambientlighting:boolean = metadata.ambientlighting;
                if (ambientlighting === true) {
                    if (metadata.hasOwnProperty("ambientskycolor")) {
                        const lightname:string = "Ambient Light";
                        let ambientlight:BABYLON.HemisphericLight = this._loader.babylonScene.getLightByName(lightname) as BABYLON.HemisphericLight;
                        if (ambientlight == null) {
                            ambientlight = new BABYLON.HemisphericLight(lightname, new BABYLON.Vector3(0, 1, 0), this._loader.babylonScene);
                        }
                        ambientlight.diffuse = BABYLON.Utilities.ParseColor3(metadata.ambientskycolor);
                        if (metadata.hasOwnProperty("ambientlightintensity")) { 
                            ambientlight.intensity = metadata.ambientlightintensity;
                        }
                        if (metadata.hasOwnProperty("ambientspecularcolor")) { 
                            ambientlight.specular = BABYLON.Utilities.ParseColor3(metadata.ambientspecularcolor);
                        }
                        if (metadata.hasOwnProperty("ambientgroundcolor")) { 
                            ambientlight.groundColor = BABYLON.Utilities.ParseColor3(metadata.ambientgroundcolor);
                        }
                    }
                }
            }
            // ..
            // Setup Scene Physics Engine Library
            // ..
            if (metadata.hasOwnProperty("enablephysics")) {
                let enablephysics:boolean = metadata.enablephysics;
                if (enablephysics === true) {
                    // TODO: Setup Physics And Gravity
                    // console.log("CVTOOLS - Parse Physics Engine Properties");
                }
            }
            // ..
            // TODO: Load Six-Sided Skybox Textures
            // ..
            // TODO: Load HDR-DDS Environment Texture
            // ..
            // TODO: Load Navigation Mesh Interfaces
        }
        // ..
        return this._loader.loadSceneAsync(context, scene);
    }

    /** @hidden */
    public loadNodeAsync(context: string, node: BABYLON.GLTF2.Loader.INode, assign: (babylonMesh: BABYLON.Mesh) => void): BABYLON.Nullable<Promise<BABYLON.Mesh>> {
        return this._loader.loadNodeAsync(context, node, (mesh: BABYLON.Mesh) => {
            if (node.extras != null && (<any>node.extras).metadata != null) {
                const metadata:any = (<any>node.extras).metadata;
                // ..
                // console.log("CVTOOLS - Parsing Mesh Node: " + mesh.name);
                // console.log(metadata);
                // ..
                if (mesh.name == null || mesh.name === "") {
                    mesh.name = "Mesh." + mesh.id;
                }
                mesh.metadata = metadata;
                if (metadata.components != null) {
                    const components:any[] = metadata.components;
                    // ..
                    // Parse Node Components
                    // ..
                    if (components != null && components.length > 0) {
                        let scripts:any[] = [];
                        components.forEach((component:any) => {
                            if (component != null) {
                                switch (component.alias) {
                                    case "script":
                                        scripts.push(component);
                                        break;
                                    case "camera":
                                        CVTOOLS_unity_metadata.SetupCameraComponent(mesh, component, this._loader.babylonScene);
                                        break;
                                    case "light":
                                        CVTOOLS_unity_metadata.SetupLightComponent(mesh, component, this._loader.babylonScene);
                                        break;
                                }
                            }
                        });
                        // ..
                        // TODO: Store In Global List Then After All Nodes Process Add Script Components in Script Executtion Order
                        // ..
                        if (scripts.length > 0) {
                            //scripts.forEach((script) => { CanvasTools.ParseScriptNode(app, entity, data, resources, script); });
                        }
                    }
                }
            }
            // ..
            assign(mesh);
        });
    }

    /** @hidden */
    public loadMaterialPropertiesAsync(context: string, material: BABYLON.GLTF2.Loader.IMaterial, babylonMaterial: BABYLON.Material): BABYLON.Nullable<Promise<void>> {
        const promises = new Array<Promise<any>>();
        promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial));
        // ..
        // console.log("CVTOOLS - Parsing Material Info: " + material.name);
        // console.log(material);
        // ..
        // Parse Material Lightmap Textures
        // ..
        const materialJson:any = material;
        const commonConstant:any = (materialJson.hasOwnProperty("commonConstant")) ? materialJson.commonConstant : null;
        if (commonConstant != null) {
            if (commonConstant.lightmapTexture) {
                promises.push(this._loader.loadTextureInfoAsync(`${context}/lightmapTexture`, commonConstant.lightmapTexture, (texture) => {
                    if (babylonMaterial instanceof BABYLON.PBRMaterial) {
                        let pbrMaterial:BABYLON.PBRMaterial = babylonMaterial as BABYLON.PBRMaterial;
                        pbrMaterial.lightmapTexture = texture;
                        pbrMaterial.useLightmapAsShadowmap = true;
                    } else if (babylonMaterial instanceof BABYLON.StandardMaterial) {
                        let stdMaterial:BABYLON.StandardMaterial = babylonMaterial as BABYLON.StandardMaterial;
                        stdMaterial.lightmapTexture = texture;
                        stdMaterial.useLightmapAsShadowmap = true;
                    }
                }));
            }
        }
        // ..
        // TODO: Parse Terrrain Splatmap Textures
        // ..
        return Promise.all(promises).then(() => { });
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    // Canvas Tools Private Parsing Worker Functions
    //////////////////////////////////////////////////////////////////////////////////////////////////////

    private static SetupCameraComponent(mesh:BABYLON.Mesh, component:any, scene:BABYLON.Scene):void {
        let label:string = mesh.name + ".Rig";
        let camera:BABYLON.UniversalCamera = new BABYLON.UniversalCamera(label, BABYLON.Vector3.Zero(), scene);
        camera.parent = mesh;
        camera.rotation = new BABYLON.Vector3(0, Math.PI, 0);
        camera.checkCollisions = false;
        let cameratype:number = component.hasOwnProperty("projection") ? component.projection : 0;
        switch (cameratype) {
            case 0: { // PERSPECTIVE
                camera.mode = BABYLON.Camera.PERSPECTIVE_CAMERA;
                if (component.hasOwnProperty("perspectiveyfov")) {
                    camera.fov = component.perspectiveyfov;
                }
                if (component.hasOwnProperty("perspectiveznear")) {
                    camera.minZ = component.perspectiveznear;
                }
                if (component.hasOwnProperty("perspectivezfar")) {
                    camera.maxZ = component.perspectivezfar;
                }
                break;
            }
            case 1: { // ORTHOGRAPHIC:
                camera.mode = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;
                if (component.hasOwnProperty("orthoxmag")) {
                    camera.orthoLeft = -component.orthoxmag;
                    camera.orthoRight = component.orthoxmag;
                }
                if (component.hasOwnProperty("orthoymag")) {
                    camera.orthoBottom = -component.orthoymag;
                    camera.orthoTop = component.orthoymag;
                }
                if (component.hasOwnProperty("orthoznear")) {
                    camera.minZ = component.orthoznear;
                }
                if (component.hasOwnProperty("orthozfar")) {
                    camera.maxZ = component.orthozfar;
                }
                break;
            }
        }
        // ..
        // Attach Camera Rig To Mesh
        // ..
        (<any>mesh).cameraRig = camera;
    }

    private static SetupLightComponent(mesh:BABYLON.Mesh, component:any, scene:BABYLON.Scene):void {
        console.log("===> TODO: Create Node Light Rig: " + mesh.name);
    }
}
BABYLON.GLTF2.GLTFLoader.RegisterExtension(CVTOOLS_NAME, (loader) => new CVTOOLS_unity_metadata(loader));

If that helps in any way to get your stuff going :slight_smile:

2 Likes

This can be done by adding an issue on github, right?

Thanks @MackeyK24. I’ll try this understand your class and adapt to my requirements.

Yes, open an issue here: Issues · BabylonJS/Babylon.js · GitHub