how to get a list of materials from a model without displaying the model

hello,
I’m familiar with sketchfab, I’m preparing a bed configurator, the plan is to keep a list of materials in a separate model, e.g. with id: 7afbd2657d1a4e1184819bbe0da02c9c, as a sample book, which I want to use in the configurator to show a preview of the result

I customized the configurator using the Viewer API, I show and hide nodes, for example, model with id: b9b13dcb81564bc7b877a4c63cef4380

is there a possibility to read the list of materials without having to go through this process via the Viewer API?

now I do it by calling two models (I hide the sample book with css) in order to use the Viewer API api.getMaterialList to get a list of models, which I will then use on the bed part using api.assignMaterial

Hello there,
I ran into another wall when I want to use a material from another model, so that I copy the field of materials in one and then use api.createMaterial to create new materials, so they lack textures, I decided to extract them in a similar way and use them, but I don’t know how to assign a texture to the newly created material.

I am also attaching the code, for clarification:

Selection of materials and textures from model 1:

let LVmaterials;
let LVtextures;

var getAllTexture = function (modelID) {
    let iframe = document.getElementById('api-frame2');
    //let modelID = 'e9f78b372c0e4e239faf2c329895832b';

    let client = new Sketchfab(iframe);
    client.init(modelID, {
        success: function onSuccess(api) {
            api.start();
            api.addEventListener('viewerready', function () {

               api.getMaterialList(function (err, materials) {
                    if (!err) {

                        console.log(materials);
                        LVmaterials = materials;

                    }
                });

                api.getTextureList(function (err, textures) {
                    if (!err) {
                        console.log(textures);
                        LVtextures = textures;
                    }
                });

            }); // wiewer ready end                
        },
        error: function onError() {
            console.log('Viewer error');
        }
    });
}

getAllTexture('7afbd2657d1a4e1184819bbe0da02c9c');

Use in model 2:

// add texture from model 1 
for (let a = 0; a < LVtextures; a++) {
                    api.addTexture(LVtextures[a].images[0].url, function(err, textureUid) {
                    if (!err) {
                        window.console.log('New texture registered with UID', textureUid);
                    }
                });

// create material
for (let m = 0; m < LVmaterials.length; m++) {
                 
                    api.createMaterial(
                        LVmaterials[m], 
                        function(err, material) {
                        if (!err) {
                            window.console.log(material);
                            let buttonMaterialu = document.createElement('button');
                            buttonMaterialu.addEventListener('click', function () {
                                setMaterial(material.id);                      
                            }, false);
                            buttonMaterialu.innerText = material.name;
                            buttonMaterialu.classList = 'btn btn-material';
                            vypis.appendChild(buttonMaterialu);                           
                        }
                    });
                }

                api.getMaterialList(function (err, materials) {
                    if (!err) {
                        console.log(materials);
                    }
                });

                function setMaterial(coTamD) {
                    let withMaterialId = {};
                    api.getNodeMap(function(err, nodes) {
                        if (!err) {
                            console.log("nodes: ");
                            console.log(nodes);  
                            
                            Object.entries(nodes).forEach((entry)=>{
                                if (entry[1].materialID === "ebc02f36-4394-4df1-bf45-9fe374afe4ac") withMaterialId[entry[0]] = entry[1];                             
                            });
                        
                            Object.entries(withMaterialId).forEach((entry)=>{
                                api.assignMaterial(entry[1], coTamD, function(err) {
                                    if (!err) {                            
                                        window.console.log('Material assigned');
                                    }
                                });                    
                            });

                        }
                    });

                    //help for setting up the material, without the need to click on the scene              
                    if (pmockaVelkosti == 45) {
                        api.setFov(46.0201, function(err, angle) {
                            if (!err) {
                                window.console.log('FOV set to', angle); // 45
                                pmockaVelkosti = 45.0001;
                            }
                        });
                    } else {
                        api.setFov(45, function(err, angle) {
                            if (!err) {
                                window.console.log('FOV set to', angle); // 45
                                pmockaVelkosti = 45;
                            }
                        });
                    }
                }

So the question is: is it possible to transfer material from one model to another?

Hi @vulpius,
loading a Sketchfab model without actually showing it means you’d have to trick the browser. Usually when a sketchfab iframe is not visible (e.g. offscreen or covered) the model stops loading. I’m not sure if you have already solved this, but you could try to load your material-scene really tiny, like 1x1 pixel.

Regarding the textures: a texture is only available in the API is it’s actually used in a material in the scene. If you upload a bunch of textures in your sketchfab scene, but only add one of them to the albedo channel for instance, only the applied texture is available in the API. The other ones are just not loaded.
Apart from that, I’m unsure whether you can get a texture ID from one scene and use it in another. I actually would assume not.
Perhaps @james has some insight?

1 Like

hi, thanks for your reply @klaasnienhuis

no, I tried that at the beginning, then I assigned the material and texture field to a variable, which I then work with as a source for creating a new material

as I showed in the example above

for (let m = 0; m < LVmaterials.length; m++) {
                 
                    api.createMaterial(
                        LVmaterials[m], 
                        function(err, material) {
                        if (!err) {
                            window.console.log(material);

@vulpius here’s a codepen that copies a material between scenes: https://codepen.io/klaasnienhuis/pen/jOKaama/1bb15fbf9374644b8aadfe418e09461f
Perhaps it helps

1 Like

hello, I have incorporated my concrete ideas and nodes into your example, the material will not be transferred even with its texture or image, I want to get the data, for example, oak

try running this code on Your CodePen (https://codepen.io/klaasnienhuis/pen/jOKaama/1bb15fbf9374644b8aadfe418e09461f):

const getMaterialList = (api) => {
            return new Promise((resolve) => {
                api.getMaterialList(function (err, materialList) {
                    if (!err) resolve(materialList);
                });
            });
        };

        const getNodeMap = (api) => {
            return new Promise((resolve) => {
                api.getNodeMap(function (err, nodes) {
                    if (!err) resolve(nodes);
                });
            });
        };

        // We're looking for a geometry node. Please note that Sketchfab
        // sometimes alters the names of your objects by appending indices.
        // That's why I'm using indexOf to match the name.
        const getNode = (nodemap, nodename) => {
            return Object.values(nodemap).find((node) => {
                if (node.type === "Geometry" && node.name.indexOf(nodename) === 0) {
                    return node;
                }
            });
        };

        const assignMaterial = (api, node, materialdefinition) => {
            return new Promise((resolve) => {
                api.createMaterial(
                    { channels: materialdefinition },
                    function (err, material) {
                        api.assignMaterial(node, material.id, function (err) {
                            resolve();
                        });
                    }
                );
            });
        };

        let sourceMaterial = {};
        function sourceCallback(api) {
            api.start(function () {
                api.addEventListener("viewerready", function () {
                    getMaterialList(api).then((materialList) => {
                        sourceMaterial = materialList.find((mtl) => mtl.name === "DUB HALIFAX TABAK");
                        console.log(sourceMaterial);
                    });
                });
            });
        }

        function targetCallback(api) {
            let targetObject = {};
            api.start(function () {

                api.addEventListener("viewerready", function () {
                    getNodeMap(api).then((nodemap) => {
                        targetObject = getNode(nodemap, "VC_LUCKA");
                        console.log(targetObject);

                        let lvHide = false;
                        let lvShow = true;

                        function showHideNodes(action, arrayOfNodes) {
                            if (action) {
                                for (let i = 0; i < arrayOfNodes.length; i++) {
                                    api.show(arrayOfNodes[i]);
                                }
                            } else {
                                for (let i = 0; i < arrayOfNodes.length; i++) {
                                    api.hide(arrayOfNodes[i]);
                                }
                            }
                        }

                        showHideNodes(lvHide, [2739, 2765, 2791, 655, 35, 3, 38, 53, 68, 83, 110, 137, 179, 194, 221, 432]);
                        showHideNodes(lvShow, [35, 36, 37, 164]);
                    });

                    document
                        .getElementById("doTransfer")
                        .addEventListener("click", function () {
                            assignMaterial(api, targetObject, sourceMaterial.channels);
                            console.log('Material assigned')
                        });
                });
            });
        }

        function loadSketchfab(sceneuid, elementId, callback) {
            const version = "1.12.1";
            const iframe = document.getElementById(elementId);
            const client = new Sketchfab(version, iframe);
            const playerOptions = {
                ui_stop: 0,
                ui_inspector: 0,
                ui_infos: 0,
                ui_controls: 0,
                watermark: 0,
                annotations_visible: 0
            };

            client.init(sceneuid, {
                error: () => console.error("Sketchfab API error"),
                success: callback,
                ...playerOptions
            });
        }

        loadSketchfab(
            "7afbd2657d1a4e1184819bbe0da02c9c",
            "api-frame-source",
            sourceCallback
        );
        loadSketchfab(
            "0482834ec9b44ab4b613fd74a0723770",
            "api-frame-target",
            targetCallback
        );

won’t it be a mistake that the textures and thus the images from the flood material were not transferred there? thanks for the reply or suggestion!

Hi @vulpius I think we’ve already established that transferring textures just like that isn’t going to work. You’d need to get the texture from the source scene and add it to the target scene first, before you can use it.

thank you for your help, I just have one last question and it will be done

I did it by downloading the array of models to json, which I use when initializing the model to create materials

when I look at the textures in the sample book, one texture has N images in the field

Object

colorSpace: "unknown"

createdAt: "2022-11-15T18:53:25.798237"

images: Array (7)
0 {uid: "8e2d3424dcb944e59e4d2e69cd9d4c4a", size: 1516, width: 64, height: 64, url: "https://media.sketchfab.com/models/7afbd2657d1a4e1…8c7024dd644/8e2d3424dcb944e59e4d2e69cd9d4c4a.jpeg", …}
1 {uid: "c4cf0f2818994a5db3e10dc1b631d01c", size: 8488, width: 128, height: 128, url: "https://media.sketchfab.com/models/7afbd2657d1a4e1…8c7024dd644/c4cf0f2818994a5db3e10dc1b631d01c.jpeg", …}
2 {uid: "9589cd740ebc4d959b7c7356265d840d", size: 49492, width: 256, height: 256, url: "https://media.sketchfab.com/models/7afbd2657d1a4e1…8c7024dd644/9589cd740ebc4d959b7c7356265d840d.jpeg", …}
3 {uid: "9d5e8809ccfc40b9ba2920bc5001be55", size: 211993, width: 512, height: 512, url: "https://media.sketchfab.com/models/7afbd2657d1a4e1…8c7024dd644/9d5e8809ccfc40b9ba2920bc5001be55.jpeg", …}
4 {uid: "44a529ef85eb4b119047cc52945aa182", size: 778873, width: 800, height: 800, url: "https://media.sketchfab.com/models/7afbd2657d1a4e1…8c7024dd644/44a529ef85eb4b119047cc52945aa182.jpeg", …}
5 {uid: "f7512b246c96400591c70209f7f3a5ac", size: 488321, width: 512, height: 512, url: "https://media.sketchfab.com/models/7afbd2657d1a4e1…8c7024dd644/f7512b246c96400591c70209f7f3a5ac.jpeg", …}
6 {uid: "94093c499d9c483eb588967bbab6760e", size: 1452, width: 32, height: 32, url: "https://media.sketchfab.com/models/7afbd2657d1a4e1…8c7024dd644/94093c499d9c483eb588967bbab6760e.jpeg", …}

Prototyp Array

name: "ASTON_WR_08_Mustard.jpg"

uid: "14b7f351af5047a2a4fa18c7024dd644"

updatedAt: "2022-11-15T18:54:01.535221"

can I use api to set N images into one texture when creating a texture? now I’m taking the first from the field and the result doesn’t look very good, [look at this link](http://wskonf.vulpius.sk/material.html)

Sketchfab creates an image pyramid for every image that you upload. e.g. if you upload a 1024px image, sketchfab will create versions at smaller sizes to be used at different distances: 512, 256, 128 and so on. So: you upload one image, Sketchfab creates those others.
If you want to download the original image, you’d need to find the images with the largest size from that array (probably).