Reputation: 335
I'm trying to render a 3D model in my React Native app using Expo and Three.js. The model loads, but it's completely black, and the texture isn't wrapping properly. I've tried adjusting the material and lighting, but I'm still having issues. Here's my code:
import React, { useState, useRef } from "react";
import { View } from "react-native";
import { GLView } from "expo-gl";
import { Renderer } from "expo-three";
import * as THREE from "three";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader";
import { Asset } from "expo-asset";
const ThreeJSModelViewer: React.FC = () => {
const [modelLoaded, setModelLoaded] = useState(false);
const [error, setError] = useState<string | null>(null);
const objectRef = useRef<THREE.Object3D | null>(null);
const handleContextCreate = async (gl: any) => {
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
const renderer = new Renderer({ gl });
renderer.setSize(width, height);
renderer.setClearColor("#ffffff", 0);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
camera.position.z = 7;
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
try {
const objAsset = Asset.fromModule(require("../../assets/temp-obj/ibraheem.obj"));
const mtlAsset = Asset.fromModule(require("../../assets/temp-obj/ibraheem.mtl"));
const textureAsset = Asset.fromModule(require("../../assets/temp-obj/ibraheem.png"));
await Promise.all([objAsset.downloadAsync(), mtlAsset.downloadAsync(), textureAsset.downloadAsync()]);
const mtlLoader = new MTLLoader();
const materials = await mtlLoader.loadAsync(mtlAsset.uri);
materials.preload();
const objLoader = new OBJLoader();
objLoader.setMaterials(materials);
const object = await objLoader.loadAsync(objAsset.uri);
const textureLoader = new THREE.TextureLoader();
const texture: THREE.Texture = await new Promise((resolve, reject) => {
textureLoader.load(
textureAsset.uri,
(loadedTexture) => resolve(loadedTexture),
undefined,
(error) => reject(error)
);
});
object.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.material = new THREE.MeshPhongMaterial({
map: texture,
specular: new THREE.Color(0x111111),
shininess: 200,
});
}
});
// Resize and position the object
const resizeObject = (obj: THREE.Object3D) => {
const box = new THREE.Box3().setFromObject(obj);
const size = box.getSize(new THREE.Vector3());
const maxDim = Math.max(size.x, size.y, size.z);
const scale = 5 / maxDim;
obj.scale.multiplyScalar(scale);
box.setFromObject(obj);
box.getCenter(obj.position);
obj.position.multiplyScalar(-1);
};
resizeObject(object);
scene.add(object);
setModelLoaded(true);
objectRef.current = object;
const animate = () => {
requestAnimationFrame(animate);
if (objectRef.current) {
objectRef.current.rotation.y += 0.01;
}
renderer.render(scene, camera);
gl.endFrameEXP();
};
animate();
} catch (error) {
console.error("Error loading 3D model:", error);
setError("Failed to load 3D model.");
}
};
return (
<View style={{ flex: 1 }}>
<GLView style={{ flex: 1 }} onContextCreate={handleContextCreate} />
{/* ... (other UI components) */}
</View>
);
};
export default ThreeJSModelViewer;
Here's the content of my .mtl file:
newmtl FaceTexture
map_Kd ibraheem.png
The texture file (ibraheem.png) exists and is in the correct location.
I'm not sure what I'm doing wrong. Any help would be greatly appreciated!
Increasing the intensity of ambient and directional lights Using different material types (MeshBasicMaterial, MeshStandardMaterial) Checking if the texture is loading correctly Verifying the .obj and .mtl files are correctly formatted
Despite these attempts, the model remains black and the texture isn't applied. What am I missing?
Upvotes: 1
Views: 58
Reputation: 194
Use ExpoTHREE.TextureLoader(). This solved my problem :
import ExpoTHREE, { THREE,Renderer, TextureLoader } from 'expo-three';
import { Asset, useAssets } from 'expo-asset';
const asset = Asset.fromModule(require('../../assets/images/es.png'));
const texture = new ExpoTHREE.TextureLoader().load(asset)
console.log("texture:", texture);
const textureMaterial = new THREE.MeshBasicMaterial({ map: texture });
Upvotes: 0