The model scale is too small when using in web

0

I’m trying to embed a Sketchfab 3D model in my React JSX hero section, but every time I do, the model appears extremely small—like a single pixel. However, the model remains tiny. How can I properly size and scale the 3D model inside my hero section so it fits well and is fully visible? Traveler - Download Free 3D model by maxpanysh [f86f5f2] - Sketchfab
im trying to use that exact model directed by the link. Other models appear fine but not this.

Any help is appreciated!

import React, { Suspense } from “react”;
import { Canvas } from ‘@react-three/fiber’;
import { useGLTF, Environment, OrbitControls, Html } from ‘@react-three/drei’;
import “./HeroModel3D.css”;

// Simplified model component using useGLTF
function PiggyModel(props) {
const { scene } = useGLTF(‘/piggymodel/Pbr/traveler.glb’);
return <primitive object={scene} {…props} />;
}

// Loader component for displaying during model loading
function Loader() {
return (



Loading 3D model…




);
}

// Main component
const HeroModel3D = () => {
return (


<Canvas
camera={{ position: [5, 5, -5], fov: 25 }}
style={{ background: “transparent” }}
>
<color attach=“background” args={[‘#101010’]} />

<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} intensity={1} castShadow />

    <Suspense fallback={<Loader />}>
      <PiggyModel position={[-0.1, -0.5, 0]} rotation={[0, Math.PI / 2, 0]} scale={1} />
      <Environment preset="city" />
      <OrbitControls 
        minPolarAngle={Math.PI / 2.5} 
        maxPolarAngle={Math.PI / 2.5}
        autoRotate={true}
        autoRotateSpeed={1}
      />
    </Suspense>
  </Canvas>
</div>

);
};

export default HeroModel3D;


this is what it looks like right now


when use a different model it looks fine

I used this code to load Sketchfab models, which allows you to supply a bounding box and the model will be loaded into that box. This creates a hidden input element and auto-clicks it, but you can do it other ways if you already have such an element. (I’m not using React, and this is triggered by some other action)

let loadGLBModel = function(bounds = { min: { x: -5, y: 0, z: -5 }, max: { x: 5, y: 5, z: 5 } }) {
  // Create a file input element
  const fileInput = document.createElement('input');
  fileInput.type = 'file';
  fileInput.accept = '.glb';
  fileInput.style.display = 'none';
  document.body.appendChild(fileInput);
  
  // Trigger file selection dialog
  fileInput.click();
  
  // Handle file selection
  fileInput.addEventListener('change', function(event) {
    const file = event.target.files[0];
    if (!file) {
      document.body.removeChild(fileInput);
      return;
    }
    
    const fileURL = URL.createObjectURL(file);
    
    gltfLoader.load(fileURL, function(gltf) {
      // Remove previous model if it exists
      if (currentModel) {
        scene.remove(currentModel);
      }
      
      const model = gltf.scene;
      currentModel = model;
      
      // Calculate model dimensions
      const boundingBox = new THREE.Box3().setFromObject(model);
      const size = new THREE.Vector3();
      boundingBox.getSize(size);
      
      // Calculate target dimensions
      const targetSize = {
        x: bounds.max.x - bounds.min.x,
        y: bounds.max.y - bounds.min.y,
        z: bounds.max.z - bounds.min.z
      };
      
      // Calculate scale factor preserving aspect ratio
      const scaleX = targetSize.x / size.x;
      const scaleY = targetSize.y / size.y;
      const scaleZ = targetSize.z / size.z;
      const scale = Math.min(scaleX, scaleY, scaleZ);
      
      // Apply uniform scaling
      model.scale.set(scale, scale, scale);
      
      // Re-calculate bounding box after scaling
      const scaledBox = new THREE.Box3().setFromObject(model);
      const center = new THREE.Vector3();
      scaledBox.getCenter(center);
      
      const targetCenter = new THREE.Vector3(
        (bounds.min.x + bounds.max.x) / 2,
        bounds.min.y,  // Use minimum Y to place on ground
        (bounds.min.z + bounds.max.z) / 2
      );
      
      // Calculate the offset differently for Y to align bottom of model with ground
      model.position.x = targetCenter.x - center.x;
      model.position.y = targetCenter.y - (scaledBox.min.y - model.position.y); // Align bottom with ground
      model.position.z = targetCenter.z - center.z;
      
      // Add to scene and start animation loop if not already running
      scene.add(model);
      
      if (!animationLoopRunning) {
        animationLoopRunning = true;
        renderer.setAnimationLoop(animate);
      }
      
      console.log(`Model "${file.name}" loaded and scaled to fit bounds`);
      
      // Clean up
      URL.revokeObjectURL(fileURL);
      document.body.removeChild(fileInput);
      
    }, function(xhr) {
      // Progress callback
      console.log(`${(xhr.loaded / xhr.total * 100).toFixed(2)}% loaded`);
    }, function(error) {
      // Error callback
      console.error('Error loading model:', error);
      URL.revokeObjectURL(fileURL);
      document.body.removeChild(fileInput);
    });
  });
  
  // Clean up if dialog is closed without selection
  fileInput.addEventListener('cancel', function() {
    document.body.removeChild(fileInput);
  });
};

Try wrap model in that BB for skinnedmesh propsed by @donmccurdy

const vector = new THREE.Vector3();
const box = new THREE.Box3().makeEmpty();
const position = mesh.geometry.attributes.position;

for ( let i = 0, il = position.count; i < il; i ++ ) {

	vector.fromBufferAttribute( position, i );
	mesh.boneTransform( i, vector );
	mesh.localToWorld( vector );
	box.expandByPoint( vector );
}

And secondly, keep in mind that model [maybe] was exported from software with incorrect scale settings. Try import that model to Three.js editor, then will appear exact the same, small size.

1 Like