Using FilesInput to handle drag and drop not working as expected

Hi everyone! I’m building a gLTF viewer similar to the Babylon Sandbox.

The user should be able to drag and drop files (gltf, bin, image assets) to the canvas and they should render. The problem is that the files seemingly load, but nothing happens in terms of adding them to the scene.

Here is a playground I extended to show what I mean.

What I expect is for reload to be called and the dropped model to be present. What I get is the scene reloads and there is no model present.

Any advice? Thanks all.

Hi bdupree and welcome to the Babylon.js forum!

In your playground the callback given as the 3rd argument to BABYLON.FilesInput should use the 2nd argument for the scene. The 1st argument is a File.

See this fix: https://playground.babylonjs.com/#7PV3QQ#8.

2 Likes

Ah! Thank you for catching that. Now this works on my local machine. However…

When I am importing the sources using <script>, everything works.

When I remove the <script>s and import via npm/ES6 modules I get the error “Please provide a valid .babylon file”. Any ideas here? Maybe I should create another question specifically for this?

Try importing @babylonjs/loaders to get glTF support.

See @babylonjs/loaders - npm (npmjs.com).

Here are my imports

import { GLTFLoader } from '@babylonjs/loaders/glTF/2.0/glTFLoader'; 
import { GLTFFileLoader } from '@babylonjs/loaders/glTF/glTFFileLoader'
import { SceneLoader } from "@babylonjs/core/Loading/sceneLoader";
import '@babylonjs/loaders';
import '@babylonjs/core/Helpers/sceneHelpers';
import '@babylonjs/core/Loading/loadingScreen';

Looks good. Can you post the rest of the module?

1 Like

Sure thing. It happens in onSceneReady.

Just to reiterate – this code works when I import using

<script src="https://preview.babylonjs.com/babylon.max.js"></script>
<script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
<script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
<script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.js"></script>
<script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.js"></script>

but not when using the following npm modules.

    "@babylonjs/core": "^5.34.0",
    "@babylonjs/loaders": "^5.34.0",
    "babylonjs": "^5.34.0",
import React, { Component } from "react";
import { withModule } from "react-hoc-di";
import BaseRenderer from "../renderer/Base3DRenderer";

import { EffectLayerSceneComponent } from '@babylonjs/core/Layers/effectLayerSceneComponent'; 
import { UniversalCamera } from '@babylonjs/core/Cameras/universalCamera'; // Must import this to enable gltf loading.
import { GLTFLoader } from '@babylonjs/loaders/glTF/2.0/glTFLoader'; // Must import this to enable gltf loading.  // Must import this to enable gltf loading.
import { GLTFFileLoader } from '@babylonjs/loaders/glTF/glTFFileLoader'
import { SceneLoader } from "@babylonjs/core/Loading/sceneLoader";
import '@babylonjs/loaders';
import '@babylonjs/core/Helpers/sceneHelpers';
import '@babylonjs/core/Loading/loadingScreen';
// import { Ray } from '@babylonjs/core/Culling/ray';
import Base3DModule from "../module/Base3DModule";

class Base3DScene extends Component {

    constructor(props) {
        super(props);

        this.module = props.module;

        this.onSceneReady = this.onSceneReady.bind(this);
        this.onPointerDown = this.onPointerDown.bind(this);
        this.onPointerUp = this.onPointerUp.bind(this);

        this.renderSubject = this.module.renderSubject;
        this.sceneSubject = this.module.sceneSubject;
        this.renderRequestStream = this.module.renderRequestStream;
        this.scenePointerDownSubject = this.module.scenePointerDownSubject;
        this.scenePointerUpSubject = this.module.scenePointerUpSubject;
        this.sceneClickSubject = this.module.sceneClickSubject;
        
    }

    onPointerDown(evt, pickResult) {
        this.pointerDownTime = Date.now();
        this.renderRequestStream.on();
        this.scenePointerDownSubject.next(pickResult);
    }
    
    onPointerUp(evt, pickResult) {
        this.renderRequestStream.off();
        this.renderRequestStream.lease(2000);

        if (!this.pointerDownTime) {
            return;
        }

        this.scenePointerUpSubject.next([evt, pickResult]);

        if (this.camera) {
          // console.log(`CAMERA POSITION: ${this.camera.position}`)
          // console.log(`CAMERA RADIUS: ${this.camera.radius}`)
        }

        const offsetTimeMs = Date.now() - this.pointerDownTime;
        if (offsetTimeMs < 300) {
            this.sceneClickSubject.next([evt, pickResult]);
        }
    }

    componentDidMount() {
      BABYLON.GLTFFileLoader.IncrementalLoading = false;
      BABYLON.SceneLoader.OnPluginActivatedObservable.add((plugin) => {
          this._currentPluginName = plugin.name;
          if (this._currentPluginName === "gltf") {
              const loader = plugin
              // loader.transparencyAsCoverage = this.props.globalState.commerceMode;
              loader.validate = true;

              // loader.onExtensionLoadedObservable.add((extension: import("@babylonjs/loaders/glTF/index").IGLTFLoaderExtension) => {
              //     this.props.globalState.glTFLoaderExtensions[extension.name] = extension;
              // });

              loader.onValidatedObservable.add((results) => {
                  if (results.issues.numErrors > 0) {
                      // this.props.globalState.showDebugLayer();
                  }
              });
          }
      });

    }
    

    onSceneReady(scene) {
      this.scene = scene;
      this.scene.onPointerDown = this.onPointerDown;
      this.scene.onPointerUp = this.onPointerUp;
  
      this.engine = scene.getEngine();
      this.engine.clearInternalTexturesCache();

      this.canvas = this.engine.getRenderingCanvas();
  
      if (this.props.noRenderLoop) {
        this.engine.setHardwareScalingLevel(100);
      } else if (window.innerWidth > 500) {
        this.engine.setHardwareScalingLevel(0.7);
      } else {
        this.engine.setHardwareScalingLevel(1);
      }
  
      scene.autoClear = Boolean(this.props.forceAutoClear);
  
      this.prepareCamera()
      // this.setupCamera();
      // this.setupLights();

      this.scene.createDefaultEnvironment({
        // createGround: false,
        createSkybox: false, 
      });


      // this.scene.clearColor = new Color4(236/255, 240/255, 241/255, 1);
      // this.scene.ambientColor = new Color4(236/255, 240/255, 241/255, 1);

      this.module.sceneSubject.next(this.scene);
      this.module.cameraSubject.next(this.camera);
      this.module.engineSubject.next(this.engine);

      const filesInput = new BABYLON.FilesInput(
        this.engine,
        this.scene,
        (sceneFile, scene) => {
          scene.createDefaultCamera(true);
          scene.createDefaultLight();
          console.log("Scene loaded")
        },
        progress => {
          console.log("Progress")
          console.log(progress)
        },
        null,
        remaining => {
          console.log("Texture loaded")
          console.log(remaining)
        },
        files => {
          BABYLON.Tools.ClearLogCache();
        },
        null,
        (_, _1, message) => {
          console.log("Error")
          console.log(message)
        });

        const canvas = document.getElementById("BaseRenderer");
        filesInput.monitorElementForDragNDrop(canvas);
    }

    prepareCamera() {
      // Attach camera to canvas inputs
      if (!this.scene.activeCamera) {
          this.scene.createDefaultCamera(true);

          const camera = this.scene.activeCamera;

          if (this._currentPluginName === "gltf") {
              // glTF assets use a +Z forward convention while the default camera faces +Z. Rotate the camera to look at the front of the asset.
              camera.alpha += Math.PI;
          }

          // Enable camera's behaviors
          camera.useFramingBehavior = true;

          const framingBehavior = camera.getBehaviorByName("Framing");
          framingBehavior.framingTime = 0;
          framingBehavior.elevationReturnTime = -1;

          if (this.scene.meshes.length) {
              camera.lowerRadiusLimit = null;

              const worldExtends = this.scene.getWorldExtends(function (mesh) {
                  return mesh.isVisible && mesh.isEnabled();
              });
              framingBehavior.zoomOnBoundingInfo(worldExtends.min, worldExtends.max);
          }

          if (this.props.autoRotate) {
              camera.useAutoRotationBehavior = true;
          }

          if (this.props.cameraPosition) {
              camera.setPosition(this.props.cameraPosition);
          }

          camera.pinchPrecision = 200 / camera.radius;
          camera.upperRadiusLimit = 5 * camera.radius;

          camera.wheelDeltaPercentage = 0.01;
          camera.pinchDeltaPercentage = 0.01;
      }

      this.scene.activeCamera.attachControl();
  }
    
    setupCamera() {
      this.camera = new BABYLON.ArcRotateCamera("Camera", 3 * Math.PI / 2, -Math.PI / 2, 50, Vector3.Zero(), this.scene);
      this.camera.useFramingBehavior = true;

      // this.camera.position = new Vector3(100, 70, 136);
      // this.camera.radius = Math.max(220, 130000 * (1 / window.innerWidth));
      // this.camera.lowerRadiusLimit = 130;
      // this.camera.upperRadiusLimit = 700;
      // this.camera.panningSensibility = 0; // Prevents Right-Click Drag to Translate.
      this.camera.attachControl(this.canvas, true);
    }
    
    setupLights() {
      // this.light = new BABYLON.DirectionalLight('light', new Vector3(0, -1, .6), this.scene);
      // this.light.intensity = 1.3;
    }

    onRender(scene) {
      if (!this.camera) {
        return;
      }
      const curZoom = this.camera.inertialRadiusOffset;
      if (curZoom !== this.lastZoom) {
        if (!this.renderRequestStream.isRendering()) {
          this.renderRequestStream.lease(500);
        }
        this.scenePointerDownSubject.next();
        this.lastZoom = curZoom;
      }

      this.renderSubject.next(scene);
    }

    render() {
      let engineOptions = {};

      const style = this.props.style || {width: "100%", height: "100%"}

      return (
          <BaseRenderer
              id="BaseRenderer"
              className={"none"}
              style={style}
              antialias
              adaptToDeviceRatio={true}
              onSceneReady={this.onSceneReady}
              engineOptions={engineOptions}
              onRender={this.onRender}
              noRenderLoop={this.props.noRenderLoop}
              resetSceneRequestSubject={this.resetSceneRequestSubject}
              />
      )
    }
}

export default withModule(Base3DScene);

Oh crap, I got it working. I noticed that I was importing Scene,Engine, FilesInput from babylonjs and other parts from @babylonjs/core.

I removed any imports from babylonjs and replaced all with @babylonjs/core imports and it worked.

Really appreciate your time helping me here @docEdub

2 Likes