Qt3d Sceneloader:在运行时用我的自定义 material 替换实体的 material 组件
Qt3d Sceneloader: Replace the material component of an Entity with my custom material at runtime
我有以下代码使用 SceneLoader 加载 collada 场景:
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;
}
}
}
}
}
}
}
如文档中所述 (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.
在我迭代所有实体后,我尝试用代码替换 material 组件:
entityvar.components[j]=metal_mat;
但它不起作用。调试后我可以看到加载的 material 没有被替换。
如何在运行时用我的自定义 material 替换 material 组件?
我一直在努力解决这个问题。您的代码有几个问题。
entityvar.components[j]=metal_mat;
这行不通,因为 qml 要你替换整个数组,你不能就地修改它
var entitynames=sceneLoader.entityNames();
for (var i = 0; i < entitynames.length; ++i) {
var entityname=entitynames[i];
var entityvar=sceneLoader.entity(entityname);
这不适用于包含同名实体或子模型(例如根本没有名称)的文件。
以下是我解决这些问题的方法:
SceneLoader 从 fbx 或 obj 文件加载场景并将其作为子节点放置到其父节点中。所以遍历树的唯一方法就是查看父实体的childNodes数组。
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);
}
}
我有以下代码使用 SceneLoader 加载 collada 场景:
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;
}
}
}
}
}
}
}
如文档中所述 (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.
在我迭代所有实体后,我尝试用代码替换 material 组件:
entityvar.components[j]=metal_mat;
但它不起作用。调试后我可以看到加载的 material 没有被替换。
如何在运行时用我的自定义 material 替换 material 组件?
我一直在努力解决这个问题。您的代码有几个问题。
entityvar.components[j]=metal_mat;
这行不通,因为 qml 要你替换整个数组,你不能就地修改它
var entitynames=sceneLoader.entityNames();
for (var i = 0; i < entitynames.length; ++i) {
var entityname=entitynames[i];
var entityvar=sceneLoader.entity(entityname);
这不适用于包含同名实体或子模型(例如根本没有名称)的文件。
以下是我解决这些问题的方法: SceneLoader 从 fbx 或 obj 文件加载场景并将其作为子节点放置到其父节点中。所以遍历树的唯一方法就是查看父实体的childNodes数组。
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);
}
}