pmndrs / react-three-fiber

🇨🇭 A React renderer for Three.js
https://docs.pmnd.rs/react-three-fiber
MIT License
27.56k stars 1.59k forks source link

Issue with Loading OBJ File using OBJLoader in React Native #3085

Open narohan opened 1 year ago

narohan commented 1 year ago

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"
CodyJasonBennett commented 1 year ago

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} />
}
narohan commented 1 year ago

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

sicnarf14sf commented 11 months ago

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 commented 11 months ago

Hello?

CodyJasonBennett commented 11 months ago

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"
  }
}
sicnarf14sf commented 11 months ago

Thanks!

expolli commented 9 months ago

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.

Tricho340 commented 9 months ago

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.

anurbecirovic commented 9 months ago

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 (

}>

) }` Any help would be awesome ! 🙏

CodyJasonBennett commented 9 months ago

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} />
}
anurbe commented 9 months ago

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

CodyJasonBennett commented 9 months ago

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.