pmndrs / react-three-fiber

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

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

Open narohan opened 10 months ago

narohan commented 10 months 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 10 months 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 10 months 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 9 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 9 months ago

Hello?

CodyJasonBennett commented 9 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 9 months ago

Thanks!

expolli commented 7 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 7 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 7 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 7 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 7 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 7 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.