Tauseef Razaq
Tauseef Razaq

Reputation: 335

Why React Native Expo Three.js Model Texture Not Wrapping and Rendering Black?

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;

.png file (texture)

texture

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!

What I've Tried

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

Answers (1)

Lily H.
Lily H.

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

Related Questions