React-Planner集成Three.js加载MTL/OBJ文件时出现TypeError报错
React中使用react-planner加载OBJ/MTL模型报错解决
在React项目中使用react-planner包,SRC目录存储了包含MTL、OBJ和纹理文件的3D模型资源,切换至3D视图时加载报错:
Uncaught (in promise) TypeError: url.lastIndexOf is not a function
报错指向load-obj.js文件中的loadObjWithMaterial函数。
相关代码
load-obj.js中的加载函数
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'; import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'; export function loadObjWithMaterial(mtlFile, objFile, imgPath) { let mtlLoader = new MTLLoader(); mtlLoader.setResourcePath(imgPath); let url = mtlFile; return new Promise((resolve, reject) => { mtlLoader.load(url, materials => { materials.preload(); let objLoader = new OBJLoader(); objLoader.setMaterials(materials); objLoader.load(objFile, object => resolve(object)); }); }); }
调用加载函数的代码
let mtl = require('./door.mtl'); let obj = require('./door.obj'); let img = require('./texture.jpg'); return loadObjWithMaterial(mtl, obj, img) .then(object => { cached3DDoor = object; return onLoadItem(cached3DDoor.clone()) })
执行console.log(url)后,控制台输出:
Object { default: "/static/media/door.3052bdbf.mtl", … }
该路径对应的文件可正常访问。
曾尝试将let url = mtlFile;替换为let url = window.URL.createObjectURL(new Blob([mtlFile]));,但出现新报错:
Uncaught Error: THREE.OBJLoader: Unexpected line: "<!DOCTYPE html>"
问题原因与解决方法
问题根源
用require()加载MTL/OBJ文件时,webpack返回的是包含default属性的模块对象,而非直接的文件路径字符串,而MTLLoader.load()需要传入字符串格式的URL,因此触发url.lastIndexOf is not a function错误。
解决步骤
修正文件路径取值
调用函数时,传入模块对象的default属性值(即实际文件URL):let mtl = require('./door.mtl').default; let obj = require('./door.obj').default; let img = require('./texture.jpg').default; return loadObjWithMaterial(mtl, obj, img) .then(object => { cached3DDoor = object; return onLoadItem(cached3DDoor.clone()) })若使用ES6 import语法,可直接获取路径:
import mtl from './door.mtl'; import obj from './door.obj'; import img from './texture.jpg';修正纹理路径处理
MTLLoader.setResourcePath()需要的是纹理文件所在的目录路径,而非完整的纹理文件URL,需从纹理URL中提取目录部分:export function loadObjWithMaterial(mtlFile, objFile, imgUrl) { let mtlLoader = new MTLLoader(); // 提取纹理文件的目录路径 const imgPath = imgUrl.substring(0, imgUrl.lastIndexOf('/') + 1); mtlLoader.setResourcePath(imgPath); return new Promise((resolve, reject) => { mtlLoader.load(mtlFile, materials => { materials.preload(); let objLoader = new OBJLoader(); objLoader.setMaterials(materials); objLoader.load(objFile, object => resolve(object), undefined, reject); }, undefined, reject); }); }添加错误回调
在load方法中加入错误回调,便于排查后续加载问题,如上述代码中的undefined, reject参数。
内容的提问来源于stack exchange,提问作者Asif333
相关产品推荐
相关产品推荐

