google / model-viewer

Easily display interactive 3D models on the web and in AR!
https://modelviewer.dev
Apache License 2.0
6.96k stars 820 forks source link

Bug in `scale` or `getDimensions` #4167

Open simplerick opened 1 year ago

simplerick commented 1 year ago

Description

Unexpected behaviour when trying to scale a model using Puppeteer (but I believe it will be reproduced in any browser). After scaling at 1.5 times a model size becomes incredibly large according to getDimensions and getBoundingBoxCenter (see the output below). However, it looks like the problem is in the output of incorrect values for sizes, because the model is rendered as if it had been enlarged correctly.

image

Code snippet that I used to generate this output

  let evalError = await page.evaluate(async () => {
      const scale = async (modelViewerTransform) => {
        console.log(`dims: ${modelViewerTransform.getDimensions()}`);
        console.log(`bbcenter: ${modelViewerTransform.getBoundingBoxCenter()}`);
        modelViewerTransform.scale = "1.5 1.5 1.5";
        modelViewerTransform.updateFraming();
        await modelViewerTransform.updateComplete;
        console.log(`dims: ${modelViewerTransform.getDimensions()}`);
        console.log(`bbcenter: ${modelViewerTransform.getBoundingBoxCenter()}`);
      };
      const modelViewer = document.getElementById('snapshot-viewer');
      await scale(modelViewer);
  })

Model (for additional info: I transformed it with gltf-transform since it has SpecGloss PBR format): robot.glb.zip

html template:

  const defaultAttributes = {
    id: 'snapshot-viewer',
    style: `background-color: ${backgroundColor};`,
    'interaction-prompt': 'none',
    src: inputPath,
    'animation-crossfade-duration': 0,
    'interpolation-decay': 1,
  };

  validateCustomAttributes(defaultAttributes, modelViewerArgs);

  const defaultAttributesString = toHTMLAttributeString(defaultAttributes);
  const modelViewerArgsString = toHTMLAttributeString(modelViewerArgs);

  let htmlTemplate = `
    <!DOCTYPE html>
    <html>
      <head>
        <meta name="viewport" content="width=device-width, initial-scale=${devicePixelRatio}">
        <script type="module"
          src="${modelViewerUrl}">
        </script>
        <style>
          body {
            margin: 0;
          }
          model-viewer {
            --progress-bar-color: transparent;
            width: ${width}px;
            height: ${height}px;
          }
        </style>
      </head>
      <body>
        <model-viewer
          ${defaultAttributesString}
          ${modelViewerArgsString}
        />
      </body>
    </html>
  `;

Version

Browser Affected

OS

elalish commented 1 year ago

This seems to be working fine on the dev tools command line. I wonder if you forgot to wait for the load event?

simplerick commented 1 year ago

Thank you for attention, checked loaded and modelIsVisible props:

        console.log(`loaded: ${modelViewerTransform.loaded}`);
        console.log(`modelIsVisible: ${modelViewerTransform.modelIsVisible}`);
        console.log(`dims: ${modelViewerTransform.getDimensions()}`);
        console.log(`bbcenter: ${modelViewerTransform.getBoundingBoxCenter()}`);
        modelViewerTransform.scale = "1.5 1.5 1.5";
        modelViewerTransform.updateFraming();
        await modelViewerTransform.updateComplete;
        console.log(`loaded: ${modelViewerTransform.loaded}`);
        console.log(`modelIsVisible: ${modelViewerTransform.modelIsVisible}`);
        console.log(`dims: ${modelViewerTransform.getDimensions()}`);
        console.log(`bbcenter: ${modelViewerTransform.getBoundingBoxCenter()}`);
image

Web viever

I also tried to view the model at https://modelviewer.dev/editor/. Everything is fine (model is visible) if I assign camera-target manually. Here is the snippet:

<model-viewer src="robot.glb" ar ar-modes="webxr scene-viewer quick-look" camera-controls poster="poster.webp" shadow-intensity="1" scale="1.5 1.5 1.5" camera-target="0m 0.3m 0m"> </model-viewer>

But if I change camera-target to "auto auto auto" it shows nothing, presumably because it gets wrong bbox center.

elalish commented 1 year ago

Okay, so I can repro this now - it's specific to this model, or at least has something to do with its skinned mesh portion and large transforms. The first time bounds are calculated, the bindMatrixInverse down in three.js is identity, because they haven't even been applied yet. That seems wrong, yet that's what gets the correct result. The next time (when you change scale) the bind matrices have been calculated and they're tiny (based on the matrixWorld due to being in attached mode), making the inverses huge, and causing the huge and incorrect bounding box computation. This looks like a three.js bug, though likely one I helped write.

simplerick commented 1 year ago

I see, great investigation. For now, as a workaround, I avoid changing the scale of the model and change the distance in the camera-orbit.