2

I have the following code to load a collada scene using the SceneLoader:

SceneLoader{
    id: sceneLoader
    source: "file:///home/rui/projects/cad/bodyplacement_lm36_v2.dae"


    MetalRoughMaterial {

        id:metal_mat
        objectName: "MetalRoughMaterial"

        metalness: 0
        roughness: 0.9
    }


    onStatusChanged: {
        console.log("SceneLoader status: " + status);
        if (status == SceneLoader.Ready) {
            console.log("Scene is ready");

            var entitynames=sceneLoader.entityNames();
            for (var i = 0; i < entitynames.length; ++i) {

                var entityname=entitynames[i];
                var entityvar=sceneLoader.entity(entityname);

                for (var j = 0; j< entityvar.components.length; ++j) {


                    var cmp=entityvar.components[j]
                    if(cmp){
                        var cmp_class=cmp.toString();
                        if(cmp_class.indexOf("QPhongMaterial")>=0){
                            entityvar.components[j]=metal_mat;
                        }

                    }



                }



            }



        }
    }




}

As stated in docs (https://doc.qt.io/qt-5/qt3drender-qsceneloader.html#details):

The loader will try to determine the best material to be used based on the properties of the model file. If you wish to use a custom material, you will have to traverse the tree and replace the default associated materials with yours.

After I iterate all the entities I try to replace the material component with the code:

entityvar.components[j]=metal_mat;

but it isn't working. After debugging I can see that the loaded material isn't replaced.

How can I replace the material component with my custom material at runtime?

phlexity
  • 7
  • 4
Rui Sebastião
  • 855
  • 1
  • 18
  • 36
  • 1
    It looks like Qt3D's QML entity class does not provide a 'removeComponent' function which is weird. A solution that I can think of is to write a custom wrapper around the default 'QEntity' class in which you expose the 'addComponent' and 'removeComponent' function to QML and then include your custom QML class. But maybe also ask the question in the Qt forums. – Florian Blume Mar 04 '19 at 15:32

1 Answers1

3

I have been working to solve this issue. Your code has a couple of issues.

entityvar.components[j]=metal_mat;

this will not work because qml wants you to replace the whole array, you cannot modify it in place

var entitynames=sceneLoader.entityNames();
    for (var i = 0; i < entitynames.length; ++i) {
        var entityname=entitynames[i];
        var entityvar=sceneLoader.entity(entityname);

This will not work for files that have entities or submodels with identical names, such as no name at all.

Here is how I solved these issues: SceneLoader loads the scene from the fbx or obj file and places it as childNodes into its parent. So the only way to traverse the tree is to look at the parent entity's childNodes array.

import Qt3D.Render 2.12
import Qt3D.Extras 2.12
import Qt3D.Core 2.12

Entity {
    id: rootEntity

    property alias source: loader.source
    property Transform transform: Transform{}
    components:[ loader, transform ]

    SceneLoader {
        id: loader

        Component {
            id: pbrMatTemplate
            MetalRoughMaterial { }
        }

        onStatusChanged: {
            if(status == SceneLoader.Ready) {
                instantiateMaterials();
            }
        }
    }


    function instantiateMaterials() {
        // TODO create as many materials as you need here
        var newmat = pbrMatTemplate.createObject(loader);

        function traverseNodes(node) {
            if(node.components) {
                var comps = [];
                for (var j = 0; j< node.components.length; ++j) {
                    var cmp = node.components[j];
                    var cmp_class = cmp.toString();
                    if(cmp_class.indexOf("QPhongMaterial")>=0) {
                        // TODO: look up material from list of materials you created instead of just using one
                        comps.push(newMat);
                    } else {
                        comps.push(cmp);
                    }
                }
                // replace whole list of components
                node.components = comps;
            }

            if(node.childNodes) {
                for(var i=0; i<node.childNodes.length; i++) {
                    if(node.childNodes[i] == null) continue;
                    traverseNodes(node.childNodes[i]);
                }
            }
        } // traverseNodes()

        traverseNodes(rootEntity);
    }
}
heeen
  • 4,736
  • 2
  • 21
  • 24