1. When the model file only has one fbx file, the model texture displayed is different from that seen on the model platform.
2. When there are files in three formats: mtl, obj, and png, the displayed colors are inconsistent with the original model.
const ModelViewer = ({ zipUrl }) => {
const mountRef = useRef(null);
useEffect(() => {
const initScene = () => {
const width = mountRef.current.clientWidth;
const height = mountRef.current.clientHeight;
const scene = new THREE.Scene();
// scene.background = new THREE.Color(0xffffff);
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
mountRef.current.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
// Ambient Light with higher intensity
const ambientLight = new THREE.AmbientLight(0x404040, 2); // higher intensity
scene.add(ambientLight);
// Directional Light to simulate sunlight
const directionalLight = new THREE.DirectionalLight(0xffffff, 2); // higher intensity
directionalLight.position.set(5, 10, 7.5);
scene.add(directionalLight);
// Point Light to add some more brightness
// const pointLight = new THREE.PointLight(0xffffff, 2, 100); // higher intensity
// pointLight.position.set(0, 10, 10);
// scene.add(pointLight);
camera.position.z = 5;
controls.update();
// return { scene, camera, renderer, controls };
return { scene, camera, renderer };
};
const { scene, camera, renderer } = initScene();
const animate = (object) => {
// let animationId;
const render = function () {
requestAnimationFrame(render);
// animationId = requestAnimationFrame(render);
object.rotation.y += 0.01;
renderer.render(scene, camera);
};
render();
// document.addEventListener('click', function() {
// cancelAnimationFrame(animationId);
// });
};
const loadModel = async (file, fileType, textureBlob) => {
let loader;
const fileBlob = await file.async("blob");
const fileUrl = URL.createObjectURL(fileBlob);
switch (fileType) {
case 'fbx':
loader = new FBXLoader();
break;
case 'obj':
const mtlFileUrl = getMtlFileUrl(fileUrl);
const mtlBlob = await fetchMtlBlob(mtlFileUrl);
const mtlUrl = URL.createObjectURL(mtlBlob);
const mtlLoader = new MTLLoader();
mtlLoader.load(mtlUrl, (materials) => {
materials.preload();
const objLoader = new OBJLoader();
objLoader.setMaterials(materials);
objLoader.load(fileUrl, (object) => {
applyTexture(object, textureBlob);
// scene.add(object);
sizeAndAddModel(object, scene);
animate(object);
});
});
return;
case 'gltf':
case 'glb':
loader = new GLTFLoader();
break;
default:
console.error(`Unsupported file type: ${fileType}`);
return;
}
loader.load(fileUrl, (object) => {
applyTexture(object, textureBlob);
// scene.add(object);
sizeAndAddModel(object, scene);
animate(object);
});
};
const sizeAndAddModel = (object, scene) => {
const box = new THREE.Box3().setFromObject(object);
const size = box.getSize(new THREE.Vector3());
const targetSize = 4;
const scale = targetSize / Math.max(size.x, size.y, size.z);
object.scale.set(scale, scale, scale);
scene.add(object);
};
const fetchMtlBlob = async (mtlFileUrl) => {
const response = await fetch(mtlFileUrl);
return await response.blob();
};
const applyTexture = (object, textureBlob) => {
if (textureBlob) {
const textureLoader = new THREE.TextureLoader();
const textureUrl = URL.createObjectURL(textureBlob);
textureLoader.load(textureUrl, (loadedTexture) => {
object.traverse((child) => {
if (child.isMesh) {
child.material.map = loadedTexture;
child.material.needsUpdate = true;
}
});
});
}
};
const getMtlFileUrl = (objFileUrl) => {
return objFileUrl.replace('.obj', '.mtl');
};
const fetchAndUnzip = async (url) => {
const response = await fetch(url.replace(/http:///g, 'https://'));
const arrayBuffer = await response.arrayBuffer();
const zip = await JSZip.loadAsync(arrayBuffer);
let textureFile;
const modelFiles = [];
zip.forEach((relativePath, file) => {
const ext = relativePath.split('.').pop().toLowerCase();
if (ext === 'png' || ext === 'jpg' || ext === 'jpeg') {
textureFile = file;
} else if (ext === 'fbx' || ext === 'obj' || ext === 'glb' || ext === 'gltf') {
modelFiles.push({ file, fileType: ext });
} else if (ext === 'mtl') {
modelFiles.push({ file, fileType: 'mtl' });
}
});
const textureBlob = textureFile ? await textureFile.async("blob") : null;
for (const { file, fileType } of modelFiles) {
await loadModel(file, fileType, textureBlob);
}
};
fetchAndUnzip(zipUrl);
return () => {
mountRef.current.innerHTML = '';
};
}, [zipUrl]);
return <div ref={mountRef} style={{ width: 800, height: 600 }} />;
};
export default ModelViewer;
The following is the parsing of files in various formats
switch (fileType) {
case 'fbx':
loader = new FBXLoader();
break;
case 'obj':
const mtlFileUrl = getMtlFileUrl(fileUrl);
const mtlBlob = await fetchMtlBlob(mtlFileUrl);
const mtlUrl = URL.createObjectURL(mtlBlob);
const mtlLoader = new MTLLoader();
mtlLoader.load(mtlUrl, (materials) => {
materials.preload();
const objLoader = new OBJLoader();
objLoader.setMaterials(materials);
objLoader.load(fileUrl, (object) => {
applyTexture(object, textureBlob);
// scene.add(object);
sizeAndAddModel(object, scene);
animate(object);
});
});
return;
case 'gltf':
case 'glb':
loader = new GLTFLoader();
break;
default:
console.error(`Unsupported file type: ${fileType}`);
return;
}
When a 3D model contains files in three formats: mtl, obj, and png, the texture file is loaded correctly, but the display color is incorrect.