react-three-fiber icon indicating copy to clipboard operation
react-three-fiber copied to clipboard

Issue with Loading OBJ File using OBJLoader in React Native

Open orhaneroglu001 opened this issue 2 years ago • 12 comments

I encountered an issue while using the OBJLoader library from Three.js to load an OBJ file in a React Native application. I placed the OBJ file in the assets directory of my React Native project and I'm trying to load it using useLoader from the @react-three/fiber/native library.

I get the following error: Error: Could not load 2: text.indexOf is not a function (it is undefined) which comes from the line const obj = useLoader(OBJLoader, require('./assets/tableau.obj'));

My code :

import { Suspense } from 'react';
import { Canvas, useLoader } from '@react-three/fiber/native';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';

function Box() {

  const obj = useLoader(OBJLoader, require('./assets/tableau.obj'));

  return <primitive object={obj} scale={10} />
}

export default function App() {


  return (
    <Canvas>
      <ambientLight />
      <pointLight position={[1, 1, 1]} />
      <Suspense fallback={null}>
        <Box />
      </Suspense>
    </Canvas>

  );
}

node v21.1.0
"@react-three/fiber": "^8.15.9",
"@types/three": "^0.158.1",
"expo": "~49.0.15",
"expo-file-system": "~15.4.4",
"expo-gl": "~13.0.1",
"expo-status-bar": "~1.6.0",
"expo-three": "^7.0.0",
"react": "18.2.0",
"react-native": "0.72.6",
"three": "^0.158.0"

orhaneroglu001 avatar Nov 04 '23 13:11 orhaneroglu001

Seems that loaders internally are not creating the correct response type. The following is what #3086 should do:

function Box() {
  const buffer = useLoader(THREE.FileLoader, require('./assets/tableau.obj'));
  const obj = useMemo(() => new OBJLoader().parse(THREE.LoaderUtils.decodeText(buffer)), [buffer])
  return <primitive object={obj} scale={10} />
}

CodyJasonBennett avatar Nov 04 '23 15:11 CodyJasonBennett

I have another error with this code, TypeError: OBJLoader.OBJLoader.parse is not a function (it is undefined)

More informations : require('./assets/tableau.obj') return a number 1 const buffer = useLoader(THREE.FileLoader, require('./assets/table.obj')); return a empty array THREE.LoaderUtils.decodeText(buffer) logically returns an empty string

orhaneroglu001 avatar Nov 05 '23 06:11 orhaneroglu001

Hi, has this been solved already? I'm having the same issues with you. Please let me know if that has been resolved already. Thanks!

sicnarf14sf avatar Dec 01 '23 09:12 sicnarf14sf

Hello?

sicnarf14sf avatar Dec 03 '23 13:12 sicnarf14sf

I have a workaround you can try in https://github.com/pmndrs/react-three-fiber/issues/3085#issuecomment-1793472249.

function Box() {
  const buffer = useLoader(THREE.FileLoader, require('./assets/tableau.obj'));
  const obj = useMemo(() => new OBJLoader().parse(THREE.LoaderUtils.decodeText(buffer)), [buffer])
  return <primitive object={obj} scale={10} />
}

I also have a PR that would fix this issue, but it needs better integration testing since I'm already seeing fail cases with GLTFLoader.

{
  "dependencies": {
    "@react-three/fiber": "https://pkg.csb.dev/pmndrs/react-three-fiber/commit/a689a1f9/@react-three/fiber"
  }
}

CodyJasonBennett avatar Dec 03 '23 19:12 CodyJasonBennett

Thanks!

sicnarf14sf avatar Dec 09 '23 06:12 sicnarf14sf

Was able to use obj assets with this snippet:

const obj = useLoader( OBJLoader, Asset.fromModule(require("./assets/Airmax/shoe.obj")).uri )

Hope it works for you too.

expolli avatar Feb 04 '24 18:02 expolli

Was able to use obj assets with this snippet:

const obj = useLoader( OBJLoader, Asset.fromModule(require("./assets/Airmax/shoe.obj")).uri )

Hope it works for you too.

actually worked for me.

Tricho340 avatar Feb 15 '24 15:02 Tricho340

I have the same issue now. But the code from @expolli didn't work :(. Any suggestions ? import { Asset } from 'expo-asset' import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'

function Island() { const obj = useLoader( OBJLoader, Asset.fromModule(require("../assets/shoe.obj")).uri ) return <primitive object={obj} scale={10} /> }

const Home = ({ route }) => { return ( <Canvas> <ambientLight /> <pointLight position={[1, 1, 1]} /> <Suspense fallback={<View />}> <Island /> </Suspense> </Canvas> ) } Any help would be awesome ! 🙏

anurbecirovic avatar Feb 17 '24 21:02 anurbecirovic

What is the issue exactly? Do neither of these work also? I'll look into getting #3086 out, but it may not be enough on its own.

// https://github.com/pmndrs/react-three-fiber/issues/3085#issuecomment-1837583297
function A() {
  const buffer = useLoader(THREE.FileLoader, require('./assets/tableau.obj'));
  const obj = useMemo(() => new OBJLoader().parse(THREE.LoaderUtils.decodeText(buffer)), [buffer])
  return <primitive object={obj} scale={10} />
}

// Likely indicates a deeper regression since the start of the thread
// https://github.com/pmndrs/react-three-fiber/issues/3085#issuecomment-1925877969
import { Asset } from 'expo-asset'
function B() {
  const buffer = useLoader(THREE.FileLoader, Asset.fromModule(require('./assets/Airmax/shoe.obj')).uri);
  const obj = useMemo(() => new OBJLoader().parse(THREE.LoaderUtils.decodeText(buffer)), [buffer])
  return <primitive object={obj} scale={10} />
}

CodyJasonBennett avatar Feb 17 '24 22:02 CodyJasonBennett

Do you know will this work also if you want to add the material / load a material ?

function A() { const buffer = useLoader(THREE.FileLoader, require('./assets/tableau.obj')); const obj = useMemo(() => new OBJLoader().parse(THREE.LoaderUtils.decodeText(buffer)), [buffer]) return }

Looks like this example works. Thank you @CodyJasonBennett

anurbe avatar Feb 17 '24 22:02 anurbe

That example should work until #3086 is merged which lets the normal useLoader path for OBJLoader work again. Currently, loader internals return an ArrayBuffer due to how they are polyfilled instead of also a string which OBJLoader expects. GLTFLoader and more modern loaders already work OOTB.

CodyJasonBennett avatar Feb 17 '24 22:02 CodyJasonBennett