我正在尝试使用3D Force Directed Graph
中的GLTFLoader
将多维数据集的3D模型加载到three.js
。该项目使用Angular
构建。
加载模型,显示GLTFLoader: 23.507080078125ms
,但不显示对象。它进一步显示错误,
ERROR TypeError: Cannot read property 'center' of undefined
at Sphere.copy (three.module.js:5347)
at Mesh.raycast (three.module.js:14240)
at intersectObject (three.js:42091)
at Raycaster.intersectObjects (three.js:42164)
at animate (3d-force-graph.module.js:386)
at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:398)
at Object.onInvokeTask (core.es5.js:4136)
at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:397)
at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask (zone.js:165)
at ZoneTask.invoke (zone.js:460)
at timer (zone.js:1732)
加载模型的代码如下:
var manager = new THREE.LoadingManager();
var loader = new GLTFLoader(manager);
loader.load(
// resource URL
'assets/model/cube.gltf',
// called when the resource is loaded
function ( gltf ) {
gltf.scene.traverse( function ( child ) {
if ( child.isMesh ) {
child.material = gltf.materials;
}
} );
var cube = gltf.scene; // Object
self.showGraph(gData,cube,themeNum);
console.log("graph drawn");
},
// called when loading is in progresses
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened -- ' + error );
}
);
// function to draw the graph -- to be executed on 3D model load
showGraph(gData:any, cube:any, themeNum: any){
const Graph = ForceGraph3D()
(document.getElementById('3d-graph'))
.nodeThreeObject(({ group }) => new THREE.Mesh(
[
new THREE.BoxGeometry(Math.random() * 20, Math.random() * 20, Math.random() * 20),
cube,
new THREE.CylinderGeometry(Math.random() * 10, Math.random() * 10, Math.random() * 20),
new THREE.DodecahedronGeometry(Math.random() * 10),
new THREE.SphereGeometry(Math.random() * 10),
new THREE.TorusGeometry(Math.random() * 10, Math.random() * 2),
new THREE.TorusKnotGeometry(Math.random() * 10, Math.random() * 2)
][group],
new THREE.MeshLambertMaterial({
color: this.themes[themeNum][group],
transparent: true,
opacity: 0.75
})
))
.nodeAutoColorBy('group')
.onNodeClick(node => {
this.attach3DNodeClickEvent(node);
})
.graphData(gData);
}
从Blender导出的cube.gltf
文件如下:
{
"accessors" : [{
"bufferView" : 0,
"componentType" : 5121,
"count" : 36,
"max" : [23],
"min" : [0],
"type" : "SCALAR"
}, {
"bufferView" : 1,
"componentType" : 5126,
"count" : 24,
"max" : [1, 1, 1],
"min" : [-1, -1, -1],
"type" : "VEC3"
}, {
"bufferView" : 2,
"componentType" : 5126,
"count" : 24,
"max" : [1, 1, 1],
"min" : [-1, -1, -1],
"type" : "VEC3"
}],
"asset" : {
"generator" : "Khronos Blender glTF 2.0 exporter",
"version" : "2.0"
},
"bufferViews" : [{
"buffer" : 0,
"byteLength" : 36,
"byteOffset" : 0,
"target" : 34963
}, {
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 36,
"target" : 34962
}, {
"buffer" : 0,
"byteLength" : 288,
"byteOffset" : 324,
"target" : 34962
}],
"buffers" : [{
"byteLength" : 612,
"uri" : "cube.bin"
}],
"materials" : [{
"name" : "Material",
"pbrMetallicRoughness" : {
"baseColorFactor" : [0.114473, 0.362915, 0.64, 1],
"metallicFactor" : 0
}
}],
"meshes" : [{
"name" : "Cube",
"primitives" : [{
"attributes" : {
"NORMAL" : 2,
"POSITION" : 1
},
"indices" : 0,
"material" : 0
}]
}],
"nodes" : [{
"name" : "Camera",
"rotation" : [0.483536, 0.336872, -0.208704, 0.780483],
"translation" : [7.48113, 5.34367, 6.50764]
}, {
"mesh" : 0,
"name" : "Cube"
}, {
"name" : "Lamp",
"rotation" : [0.169076, 0.75588, -0.272171, 0.570948],
"scale" : [1, 1, 1],
"translation" : [4.07625, 5.90386, -1.00545]
}],
"scene" : 0,
"scenes" : [{
"name" : "Scene",
"nodes" : [1, 2, 0]
}]
}
我在这里做错了什么? three.js
文档上的示例似乎是相同的。
非常感谢任何帮助。
答案 0 :(得分:0)
根据错误,您在animate
函数中执行光线投射,这是发生错误的位置。具体来说,当光线投射过程试图复制对象的边界球时会发生错误。
// three.js r91, Mesh.js, line 237
sphere.copy( geometry.boundingSphere );
它说它无法在center
找到属性geometry.boundingSphere
,因为geometry.boundingSphere
未定义。
除非GLTFLoader
自动计算边界范围,否则此信息不会自动填充。在对象上调用raycast之前,您需要调用geometry.computeBoundingSphere()
。如果GLTFLoader
没有完成这项工作,那么您可能需要为整个场景执行此操作。
在加载数据之后(以及在启动animate
之前)运行此代码将填充所有对象'边界球:
scene.traverse(function(node){
if(node.geometry){
node.geometry.computeBoundingSphere();
}
});