The model scale is too small when using in web application project

I downloaded this model from เเมวประเทศไทย - Download Free 3D model by ann55010970637 (@ann55010970637) [dcb3a7d] - Sketchfab , The model scale is appearing normal when open in Windows 3D Viewer, three.js glTF Viewer and Babylon.js View, but when load the model in three.js module, some model’s scale is incorrect, for example.

three.js in Website

image

three.js glTF Viewer

image

Babylon.js Viewer

image

This model scale is correct, when open in another application, it scale correctly.

Model name : dog.glb

Source : GitHub - craftzdog/craftzdog-homepage: My homepage

three.js in Website

image

three.js glTF Viewer

image

Babylon.js Viewer

image

This model scale is incorrect and so tiny, but when open in another application, it scale correctly

Model name : แมวประเทศไทย

Source : เเมวประเทศไทย - Download Free 3D model by ann55010970637 (@ann55010970637) [dcb3a7d] - Sketchfab

Here is GLTF Loader Code

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

export function loadGLTFModel(
  scene,
  glbPath,
  options = { receiveShadow: true, castShadow: true }
) {
  const { receiveShadow, castShadow } = options
  return new Promise((resolve, reject) => {
    const loader = new GLTFLoader()

    loader.load(
      glbPath,
      gltf => {
        const obj = gltf.scene
        obj.name = 'persian'
        obj.position.x = 0
        obj.position.y = 0
        obj.receiveShadow = receiveShadow
        obj.castShadow = castShadow
        scene.add(obj)

        obj.traverse(function (child) {
          if (child.isMesh) {
            child.castShadow = castShadow
            child.receiveShadow = receiveShadow
          }
        })

        resolve(obj)
      },
      undefined,
      function (error) {
        reject(error)
      }
    )
  })
}

Here is Model Display Code

import { useState, useEffect, useRef, useCallback } from 'react'
import { Box, Spinner } from '@chakra-ui/react'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { loadGLTFModel } from '../lib/model'

function easeOutCirc(x) {
  return Math.sqrt(1 - Math.pow(x - 1, 4))
}

const PersianCat = () => {
  const refContainer = useRef()
  const [loading, setLoading] = useState(true)
  const [renderer, setRenderer] = useState()
  const [_camera, setCamera] = useState()
  const [target] = useState(new THREE.Vector3(-0.5, 1.2, 0))
  const [initialCameraPosition] = useState(
    new THREE.Vector3(
      20 * Math.sin(0.2 * Math.PI),
      10,
      20 * Math.cos(0.2 * Math.PI)
    )
  )
  const [scene] = useState(new THREE.Scene())
  const [_controls, setControls] = useState()

  // On component mount only one time.
  /* eslint-disable react-hooks/exhaustive-deps */
  useEffect(() => {
    const { current: container } = refContainer
    if (container && !renderer) {
      const scW = container.clientWidth
      const scH = container.clientHeight

      const renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      })
      renderer.setPixelRatio(window.devicePixelRatio)
      renderer.setSize(scW, scH)
      renderer.outputEncoding = THREE.sRGBEncoding
      container.appendChild(renderer.domElement)
      setRenderer(renderer)

      // 640 -> 240
      // 8 -> 6
      const scale = scH * 0.005 + 4.8
      const camera = new THREE.OrthographicCamera(
        -scale,
        scale,
        scale,
        -scale,
        0.01,
        50000
      )
      camera.position.copy(initialCameraPosition)
      camera.lookAt(target)
      setCamera(camera)

      const ambientLight = new THREE.AmbientLight(0xcccccc, 1)
      scene.add(ambientLight)

      const controls = new OrbitControls(camera, renderer.domElement)
      controls.autoRotate = true
      controls.target = target
      setControls(controls)

      loadGLTFModel(scene, '/persian.glb', {
        receiveShadow: false,
        castShadow: false
      }).then(() => {
        animate()
        setLoading(false)
      })

      let req = null
      let frame = 0
      const animate = () => {
        req = requestAnimationFrame(animate)
        frame = frame <= 100 ? frame + 1 : frame
        if (frame <= 100) {
          const p = initialCameraPosition
          const rotSpeed = -easeOutCirc(frame / 120) * Math.PI * 20

          camera.position.y = 10
          camera.position.x =
            p.x * Math.cos(rotSpeed) + p.z * Math.sin(rotSpeed)
          camera.position.z =
            p.z * Math.cos(rotSpeed) - p.x * Math.sin(rotSpeed)
          camera.lookAt(target)
        } else {
          controls.update()
        }

        renderer.render(scene, camera)
      }

      return () => {
        cancelAnimationFrame(req)
        renderer.dispose()
      }
    }
  }, [])

  return (
    <Box
      ref={refContainer}
      className="persian-cat"
      m="auto"
      at={['-20px', '-60px', '-120px']}
      mb={['-40px', '-140px', '-200px']}
      w={[280, 480, 640]}
      h={[280, 480, 640]}
      position="relative"
    >
      {loading && (
        <Spinner
          size="xl"
          position="absolute"
          left="50%"
          top="50%"
          ml="calc(0px - var(--spinner-size) / 2)"
          mt="calc(0px - var(--spinner-size))"
        />
      )}
    </Box>
  )
}

export default PersianCat

why incorrect? it’s just small, the view has to either anticipate it by calculating a bounding box and then implement a camera zoom-to-fit, or you fix it in blender, or statically in your code (group.scale.setScalar(factor)).
since you’re using react, use three with reavt-three-fiber, there are ready-made components for this like drei/bounds: gltf simple example - CodeSandbox most of the code above will vanish and three will be integrated and not be a second world that lives outside everything else.
this would also allow you to use things like gltfjsx GitHub - pmndrs/gltfjsx: 🎮 Turns GLTFs into JSX components

btw i noticed the model appears black even if lit, the only way to show it is to apply an environment map. materials probably have metalness=1 everywhere which most likely wasn’t the intention when exporting from blender.

Try adding the following code in your onLoad() callback of GLTFLoader. It’s from the glTF viewer.

controls.reset();

const box = new THREE.Box3().setFromObject(obj);
const size = box.getSize(new THREE.Vector3()).length();
const center = box.getCenter(new THREE.Vector3());

obj.position.x += (obj.position.x - center.x);
obj.position.y += (obj.position.y - center.y);
obj.position.z += (obj.position.z - center.z);

camera.near = size / 100;
camera.far = size * 100;
camera.updateProjectionMatrix();

camera.position.copy(center);
camera.position.x += size / 2.0;
camera.position.y += size / 5.0;
camera.position.z += size / 2.0;
camera.lookAt(center);

controls.maxDistance = size * 10;
controls.update();

You have to make sure that your loadGLTFModel() function has a reference to the camera and controls. The above snippet will center your model and move the camera to an appropriate spot.

1 Like