Imported objects have false positional values and wrong rotations

When importing an STL object and posting to scene from a file, It displays the following:

However, it states that the position of this object is (0,-0.7,0) despite the object not being located at that position, shifted on the x and z by multiple units. I did shift the y-value manually, otherwise, it would say it is at exactly (0,0,0).

When running the command rotateX(Math.PI/ 4) , it returns odd rotations that are not 180* degrees.

Same camera perspective.

It also states it is still at (0,-0.7,0).

I am using this in junction with react three fiber v7.0.6 and Three.js v0.135.0.

:thinking: The above equation is 45 degrees, Math.PI/ 2 is 90 degrees…

The issue with the y axis having a non 0 value, is the mesh / object itself part of a parent group?

1 Like

Sorry I typed the wrong angle. In the code, it was rotating by Math.PI, so 180 degrees.

The negative y-value was added by me, but was removed for the following images so it will be at 0,0,0

Despite this, the angle is still not correct.

Initial

image

Here is a rotateX(2 * Math.PI).

It doesn’t go around in a full circle, it only tips the nose of the rocket down by 45 degrees.

This is precisely at 0,0,0 according to three.

It is not in any group, nor has it been positioned. I just used react three fiber to add the model at 0,0,0

<mesh ref={meshReference} {...props} position={[0, 0, 0]} geometry={geometry}>
        <meshNormalMaterial />
</mesh>

A mesh positioned at 000 means nothing, in the end the vertices of the buffer geometry could be anywhere and will also affect rotations because you rotate not around the models volume center.

Either center the model before using it on the web or use drei/center, although that component only started working properly in later versions of drei but you can copy it into your project.

1 Like

Does this TS file need to be compiled into JS by TS since it is being used in an JSX file?

I am assuming it is the center file at: drei/Center.tsx at bbd45555813d85befffbd98efae2cd5c0ac431a8 · pmndrs/drei · GitHub

If so, where can I find the JS version?

you can just remove the types. though most bundlers also take the tsx file no problem without you having to do anything. if you use vite for instance, just copy it as is.

1 Like

If you’re not using a bundler that’s a bit long, I’d assume the Center.jsx file as part of drei at its core is simply evoking the center() method of the geometry class…

mesh.geometry.center()

If the result of this doesn’t seem correct it’s back to my original question of wether or not the mesh is in a parent group, if so you’d need to duplicate solely the geometry, center it and then generate a new mesh with the duplicated and centered geometry outside of any parent object or group, reassigning afterwards…

1 Like

I have a mesh reference to the imported geometry.

I am attempting to center and then scale it down. I used the following code, and added console logs because of the issue below. This is only a part of the React JSX element, but this is the source of the issue.

// Excerpt from the JSX element that contains the geometry

...
if (geometry && !hasBeenScaled && meshReference.current) {

      // Center the object

      console.log(meshReference.current.position, 0)
      meshReference.current.geometry.computeBoundingBox()
      console.log(meshReference.current.position, 1)
      meshReference.current.geometry.computeBoundingSphere();
      console.log(meshReference.current.position, 2)
      meshReference.current.geometry.center()
      console.log(meshReference.current.position, 3)
      meshReference.current.geometry.computeBoundingBox()
      console.log(meshReference.current.position, 4)
      meshReference.current.geometry.computeBoundingSphere();
      console.log(meshReference.current.position, 5)

      // Scale the object

      let size = new Vector3()
      meshReference.current.geometry.boundingBox.getCenter(size)
      console.log(meshReference.current.position, 6)
      let scale = 1 / Math.max(size.x, size.y, size.z)
      meshReference.current.geometry.scale(scale * 2, scale * 2, scale * 2)
      console.log(meshReference.current.position, 7)
      setHasBeenScaled(true)
   }
 
// Return for the object

   if (geometry) {
      return (
         <mesh ref={meshReference} {...props} position={[0, 0, 0]} geometry={geometry}>
            <meshNormalMaterial />
         </mesh>
      )
   } else {
      return <FallBackObject />
   }
...

This returns the console log of:

With an error code between logging 6 and 7.

index.js:1 THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The “position” attribute is likely to have NaN values.

This is even though the position is not NaN at any point.

so hang on, this implies you’re already using react? why not just use the center component as part of drei like @drcmda suggested earlier?

i think you’re getting some terms mixed up, this position attribute is referring to geometry.attributes.position not the world / local space position of the object itself…

is there any chance you can provide a live demo in sandbox / codepen / jsfiddle to allow us to see your case and assist you further?

your problem is currently here…

i’d suggest console logging size and scale just before this line to see if the values are as expected…

EDIT: of course, you’re getting the center of your bounding box, if this is 0,0,0… 0 * 2 = …0, you’re effectively setting your scale to 0, i think you’re meaning to use getSize rather than getCenter…

Edit: I have changed from code since I first posted the link.

The model file is passed as a property to the JSX. Here is what that file looks like and needs to be imported.

I am trying to simulate this by importing the model in the code below. However, in the code snippet below from the project (part of it), I can not get that to work.

I also changed it to use primitive and gave it a default box geometry to remove the error in the dev environment of the NaN position. However, I was not able to test it in the codesandbox.

However, this change also resulted in the object not being scaled or positioned by the if statement above. This is even though I made an import check via useState.

try changing the following line…


to

meshReference.current.geometry.boundingBox.getSize(size)

the sandbox has issues my side as the stl is not an arrayBuffer, it should be loaded instead with an instance of STLLoader…

With the following code, which is also in the code sandbox:

if (geometry && hasBeenImported && !hasBeenScaled && meshReference.current) {
      meshReference.current.geometry.computeBoundingBox()
      meshReference.current.geometry.computeBoundingSphere();
      meshReference.current.geometry.center()
      meshReference.current.geometry.computeBoundingBox()
      meshReference.current.geometry.computeBoundingSphere();
      let size = new Vector3()
      meshReference.current.geometry.boundingBox.getSize(size)
      console.log("Size: "+JSON.stringify(size))
      let scale = 1 / Math.max(size.x, size.y, size.z)
      console.log("Scale: "+JSON.stringify(scale))
      meshReference.current.geometry.scale(scale * 2, scale * 2, scale * 2)
      setHasBeenScaled(true)
   }

That creates the following console log

Size: {“x”:1,“y”:1,“z”:1}
Scale: 1

Despite the fact that the object is not a size of 1 (another test object).

I have attempted to create a live example of my issue on codesandbox:

But codesandbox doesn’t recognize react three fiber since the tag names are lowercase. I changed the import to be from a URL instead of trying to simulate the file property from the dev environment.

I added the following Center.js file to my project.

import { Box3, Vector3, Sphere, Group } from 'three'
import * as React from 'react'
import { useThree } from '@react-three/fiber'

export const Center = React.forwardRef(function Center(
  {
    children,
    disable,
    disableX,
    disableY,
    disableZ,
    left,
    right,
    top,
    bottom,
    front,
    back,
    onCentered,
    precise = true,
    cacheKey = 0,
    ...props
  },
  fRef
) {
  const ref = React.useRef(null)
  const outer = React.useRef(null)
  const inner = React.useRef(null)
  React.useLayoutEffect(() => {
    outer.current.matrixWorld.identity()
    const box3 = new Box3().setFromObject(inner.current, precise)
    const center = new Vector3()
    const sphere = new Sphere()
    const width = box3.max.x - box3.min.x
    const height = box3.max.y - box3.min.y
    const depth = box3.max.z - box3.min.z
    box3.getCenter(center)
    box3.getBoundingSphere(sphere)
    const vAlign = top ? height / 2 : bottom ? -height / 2 : 0
    const hAlign = left ? -width / 2 : right ? width / 2 : 0
    const dAlign = front ? depth / 2 : back ? -depth / 2 : 0

    outer.current.position.set(
      disable || disableX ? 0 : -center.x + hAlign,
      disable || disableY ? 0 : -center.y + vAlign,
      disable || disableZ ? 0 : -center.z + dAlign
    )

    // Only fire onCentered if the bounding box has changed
    if (typeof onCentered !== 'undefined') {
      onCentered({
        parent: ref.current.parent,
        container: ref.current,
        width,
        height,
        depth,
        boundingBox: box3,
        boundingSphere: sphere,
        center: center,
        verticalAlignment: vAlign,
        horizontalAlignment: hAlign,
        depthAlignment: dAlign,
      })
    }
  }, [cacheKey, onCentered, top, left, front, disable, disableX, disableY, disableZ, precise, right, bottom, back])

  React.useImperativeHandle(fRef, () => ref.current, [])

  return (
    <group ref={ref} {...props}>
      <group ref={outer}>
        <group ref={inner}>{children}</group>
      </group>
    </group>
  )
})

Then used it in the following manner.

<Canvas>
       <Center>
            <mesh ref={meshReference} {...props}>
                 <primitive object={geometry} attach="geometry" />
                  <meshNormalMaterial />
            </mesh>
      </Center>
</Canvas>

However, It is still not centered

Note that THREE.BufferGeometry.computeBoundingSphere() is still NaN and I can’t figure out how. Nothing I do is working and it is something as simple as finding the center of the shape. I can import the file, put it on the screen, but past that nothing works. Can’t center it, can’t compute the bounding sphere, can’t find the object center.

For example:

      meshReference.current.geometry.computeBoundingBox()
      meshReference.current.geometry.computeBoundingSphere();
      let size = new Vector3()
      meshReference.current.geometry.boundingBox.getCenter(size)
      console.log("Size: " + JSON.stringify(size))

size is apparently 0,0,0 for the shape above, which makes no sense

center is an expensive operation, so it avoids doing that more than once. if you give the cacheKey something unique it will center again. but in the other topic i just wrote something about suspense, read this first because that’s the clean solution.

1 Like

it does make sense if you notice you’re still using geometry.boundingBox.getCenter, as mentioned before if your geometry is already centered at 0,0,0 then getCenter would return the center as such… (0,0,0), this should probably be geometry.boundingBox.getSize to return the result you’re after…

1 Like

The centering ended up being the correct solution, along with fixing the file importing thanks to drcmda.

I wanted to add a bit of a conclusion by linking to the related topic for properly importing objects using FileReader: RangeError when loading STL File via parse, How to use FileReader for importing models - #8 by drcmda