RangeError when loading STL File via parse, How to use FileReader for importing models

Why does the parsing of the file result in a range error?

RangeError: Invalid typed array length: 15921938187

The file loads as expected in the three.js editor:

Do you mind trying it with the latest version of three.js? You are currently using r135 which is from 2021.

1 Like

When I updated to the latest version of r151.3 with the latest fiber version, how can I integrate react-three-fiber with FileReader()?

I updated the code to use a file reader instead because of the project’s circumstances. Even when I was on r.151.3, the loader still resulted in a RangeError.

Here is the import section of the codesandbox above. UseEffect never gets called and it crashes.

FileReader seems to be async, which is causing issues within the rendering process.

function Test({ savedFile }) {
  console.log("Test Is Rendered");
  console.log(savedFile);
  const meshReference = useRef(null);
  const [geometry, setGeometry] = useState(null);
  useEffect(() => {
    console.log("UseEffect Called");
    let reader = new FileReader();
    reader.addEventListener("loadend", (event) => {
      let result = event.target.result;
      let loader = new STLLoader();
      let parsedResult = loader.parse(result);
      setGeometry(parsedResult);
    });
    reader.readAsArrayBuffer(savedFile);
  }, [savedFile]);
  console.log("Rendering");
  return (
    <mesh ref={meshReference}>
      <primitive object={geometry} attach="geometry" />
      <meshBasicMaterial />
    </mesh>
  );
}

I have been doing a lot of research about using FileReader but to no solution.

there is nothing wrong with that file Model Loading Test (forked) - CodeSandbox

you use filereader just like you would always use it. put it into a useEffect like you already do. why parse doesn’t load your file no idea, maybe the parse method has a bug. or the data you’re giving it is wrong, i’m guessing it expects an arraybuffer. if you are 100% sure you’re giving it the correct input according to threejs docs i would open a bug ticket on three/github.

1 Like

Thanks for everyone’s recommendations.

Through a lot of trial and error, I figured it out.

First, I needed to use the file reader in a separate async function that would be awaited inside of the useEffect of the component. Otherwise, the file reader would start but not finish importing large files, resulting in crashes.

To fix the centering and rotation issues I was having in other threads, I found that I needed to call (on every frame via useFrame:

geometry.center() 

Which is the geometry I imported and saved as a state, rather than:

meshReference.current.geometry.center()

The top works, but the bottom one does not and produces NaN position. The scale would not work regardless, so I needed to put it as an object property on the mesh component.

you can use GitHub - pmndrs/suspend-react: 🚥 Async/await for React components

this is what all loaders in fiber use (useLoader, useGLTF, etc). if you integrate loading & parse into suspense you can have component interop.

for instance:

<Center>
  <AsyncModel />
</Center>

is only ever possible because of suspense. without that, if you load in useEffect, nothing outside of that component has the faintest idea of when the model is gonna be ready.

1 Like

Thank you for the help,

I implemented the suspense here:

However, I am noticing that it only works in this environment. The setGeometry gets run with the geometry from the file import, but after the set state call is finished, it reverts to null before hitting the return statement.

Then it doesn’t trigger a re-render of the element.

Here is an excerpt of the code:

Here is an excerpt of the console:

Found geometry is the file importer creating that geometry from the import.

The callback then receives this geometry inside of the jsx element. This is where the “changeGeometryCallBack” is located.

setState is called, but after this, you can see the state is still null.

there should be no setState. suspense returns a result and that result becomes available immediately.

imo this:

reader.addEventListener("loadend", async (event) => {

is also a different concern, it has nothing to do with the model component and should not be mixed into it. the result of that process should be handed over to the model. i just realize stlloader.parse is sync, so you just need use memo

function App() {
  const [data, set] = useState()
  useEffect(() => {
    let reader = new FileReader()
    reader.addEventListener("loadend", async (event) => set(event.target.result))
    ...
  }, [])
  return data && <Model data={data} />

function Model({ data, ...props }) {
  const geo = useMemo(() => new STLLoader().parse(data), [data])
  return <mesh geometry={geo} {...props} />
1 Like