From 9553786cd5495ebfd4e4dfd78c40b78543b71c02 Mon Sep 17 00:00:00 2001
From: Philip Rideout
Date: Tue, 13 Apr 2021 09:35:44 -0700
Subject: [PATCH] now supports optional drag-and-drop.
---
web/filament-js/filament-viewer.js | 154 ++++++++++++++++++++++++--
web/samples/test-filament-viewer.html | 9 ++
2 files changed, 154 insertions(+), 9 deletions(-)
diff --git a/web/filament-js/filament-viewer.js b/web/filament-js/filament-viewer.js
index 600dea41ba..fc74a5358e 100644
--- a/web/filament-js/filament-viewer.js
+++ b/web/filament-js/filament-viewer.js
@@ -49,7 +49,6 @@ class FilamentTasks {
// ----------------
// None of the following features are implemented. They would be easy to add.
// - Replace gltumble and Trackball with camutils Manipulator (i.e. enable scroll-to-zoom)
-// - Use decorator-style properties
// - Write a documentation page (might be neat if the doc page has instances of the actual viewer)
// - Fix the import at the top of the file to support webpack / rollup / esbuild
// - Expose more animation properties (e.g. enable / disable, selected index)
@@ -57,18 +56,24 @@ class FilamentTasks {
// - Expose more IBL properties
// - Expose directional light properties
// - Optional turntable animation
-// - Allow clients to programatically change properties
//
class FilamentViewer extends LitElement {
constructor() {
super();
- this.filamentTasks = new FilamentTasks();
- this.canvasId = "filament-viewer-canvas";
- this.src = null; // Path to glTF file. (required)
+
+ // LitElement properties:
+ this.src = null; // Path to glTF file.
this.alt = null; // Alternate canvas content.
this.ibl = null; // Path to image based light ktx.
this.sky = null; // Path to skybox ktx.
+ this.enableDrop = null; // Enables drag and drop.
this.intensity = 30000; // Intensity of the image based light.
+
+ // Private properties:
+ this.filamentTasks = new FilamentTasks();
+ this.canvasId = "filament-viewer-canvas";
+ this.overlayId = "filament-viewer-overlay";
+ this.srcBlob = null;
}
static get properties() {
@@ -77,6 +82,7 @@ class FilamentViewer extends LitElement {
alt: { type: String },
ibl: { type: String },
sky: { type: String },
+ enableDrop: { type: Boolean },
intensity: { type: Number },
}
}
@@ -92,12 +98,21 @@ class FilamentViewer extends LitElement {
return;
}
this.filamentTasks.add(this._startFilament.bind(this));
+
+ const overlay = this.shadowRoot.getElementById(this.overlayId);
+ ["dragenter", "dragover", "dragleave", "drop"].forEach(eventName => {
+ overlay.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation() }, false)
+ });
+ if (this.enableDrop) {
+ overlay.addEventListener("drop", this._dropHandler.bind(this), false);
+ }
}
updated(props) {
if (props.has("src")) this.filamentTasks.add(this._loadAsset.bind(this));
if (props.has("ibl")) this.filamentTasks.add(this._loadIbl.bind(this));
if (props.has("sky")) this.filamentTasks.add(this._loadSky.bind(this));
+ if (props.has("enableDrop")) this._updateOverlay();
if (props.has("intensity") && this.indirectLight) {
this.indirectLight.setIntensity(this.intensity);
}
@@ -110,22 +125,61 @@ class FilamentViewer extends LitElement {
width: 300px;
height: 300px;
border: solid 1px black;
+ position: relative;
}
canvas {
background: #D9D9D9; /* This is consistent with the default clear color. */
+ }
+ canvas, .overlay {
width: 100%;
height: 100%;
+ position: absolute;
+ }
+ .overlay {
+ text-align: center;
+ padding: 10px;
}`;
}
render() {
- return html``;
+ return html`
+
+
+ `;
+ }
+
+ _dropHandler(dragEvent) {
+ if (!dragEvent.dataTransfer) return;
+ this.srcBlob = null;
+ this.srcBlobResources = {};
+ for (const file of dragEvent.dataTransfer.files) {
+ if (file.name.endsWith(".glb") || file.name.endsWith(".gltf")) {
+ this.srcBlob = file;
+ } else {
+ this.srcBlobResources[file.name] = file;
+ }
+ }
+ if (this.srcBlob) {
+ this._loadAsset();
+ } else {
+ console.error("Please include a glTF file.");
+ }
+ };
+
+ _updateOverlay() {
+ const overlay = this.shadowRoot.getElementById(this.overlayId);
+ if (!this.enableDrop || this.asset) {
+ overlay.innerHTML = "";
+ } else {
+ overlay.innerHTML = "Drop glb or fileset here.";
+ }
}
_startFilament() {
const LightType = Filament.LightManager$Type;
const canvas = this.shadowRoot.getElementById(this.canvasId);
+ const overlay = this.shadowRoot.getElementById(this.overlayId);
this.engine = Filament.Engine.create(canvas);
this.scene = this.engine.createScene();
@@ -143,7 +197,7 @@ class FilamentViewer extends LitElement {
// If gltumble has been loaded, use it.
if (window.Trackball) {
- this.trackball = new Trackball(canvas, { startSpin: 0.0 });
+ this.trackball = new Trackball(overlay, { startSpin: 0.0 });
}
// This color is consistent with the default CSS background color.
@@ -218,10 +272,88 @@ class FilamentViewer extends LitElement {
}
_loadAsset() {
+ const zoffset = 4;
+
if (this.asset) {
- console.info("FilamentViewer does not allow the model to be changed.");
+ this.scene.removeEntities(this.asset.getEntities());
+ this.animator = null;
+ this.asset = null;
+ }
+
+ // If we have neither a URL nor a dropped file, leave early.
+ if (!this.src && !this.srcBlob) {
+ this._updateOverlay();
return;
}
+
+ // Dropping a glb file is simple because there are no external resources.
+ if (this.srcBlob && this.srcBlob.name.endsWith(".glb")) {
+ this.srcBlob.arrayBuffer().then(buffer => {
+ this.asset = this.loader.createAssetFromBinary(new Uint8Array(buffer));
+ const aabb = this.asset.getBoundingBox();
+ this.assetRoot = this.asset.getRoot();
+ this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
+ this.asset.loadResources();
+ this.animator = this.asset.getAnimator();
+ this.animationStartTime = Date.now();
+ this._updateOverlay();
+ });
+ return;
+ }
+
+ // Dropping a fileset requires pushing each resource to ResourceLoader.
+ if (this.srcBlob && this.srcBlob.name.endsWith(".gltf")) {
+
+ const config = {
+ normalizeSkinningWeights: true,
+ recomputeBoundingBoxes: false,
+ asyncInterval: 30
+ };
+
+ const doneAddingResources = resourceLoader => {
+ this.srcBlobResources = {};
+ resourceLoader.asyncBeginLoad(this.asset);
+ const timer = setInterval(() => {
+ resourceLoader.asyncUpdateLoad();
+ const progress = resourceLoader.asyncGetLoadProgress();
+ if (progress >= 1) {
+ clearInterval(timer);
+ resourceLoader.delete();
+ this.animator = this.asset.getAnimator();
+ this.animationStartTime = Date.now();
+ }
+ }, config.asyncInterval);
+ };
+
+ this.srcBlob.arrayBuffer().then(buffer => {
+ this.asset = this.loader.createAssetFromJson(new Uint8Array(buffer));
+ const aabb = this.asset.getBoundingBox();
+ this.assetRoot = this.asset.getRoot();
+ this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
+
+ const resourceLoader = new Filament.gltfio$ResourceLoader(this.engine,
+ config.normalizeSkinningWeights,
+ config.recomputeBoundingBoxes);
+
+ let remaining = Object.keys(this.srcBlobResources).length;
+ for (const name in this.srcBlobResources) {
+ this.srcBlobResources[name].arrayBuffer().then(buffer => {
+ const desc = getBufferDescriptor(new Uint8Array(buffer));
+ resourceLoader.addResourceData(name, getBufferDescriptor(desc));
+ if (--remaining === 0) {
+ doneAddingResources(resourceLoader);
+ }
+ });
+ }
+
+ this._updateOverlay();
+ });
+
+ return;
+ }
+
+ // If we reach this point, we are loading from a src URL rather than drag-and-drop.
+
fetch(this.src).then(response => {
return response.arrayBuffer();
}).then(arrayBuffer => {
@@ -231,8 +363,10 @@ class FilamentViewer extends LitElement {
} else {
this.asset = this.loader.createAssetFromJson(modelData);
}
+
+ const aabb = this.asset.getBoundingBox();
this.assetRoot = this.asset.getRoot();
- this.unitCubeTransform = Filament.fitIntoUnitCube(this.asset.getBoundingBox(), 4);
+ this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset);
const basePath = '' + new URL(this.src, document.location);
@@ -240,6 +374,8 @@ class FilamentViewer extends LitElement {
this.animator = this.asset.getAnimator();
this.animationStartTime = Date.now();
}, null, basePath);
+
+ this._updateOverlay();
});
}
diff --git a/web/samples/test-filament-viewer.html b/web/samples/test-filament-viewer.html
index cc25a47cb2..df10b867a8 100644
--- a/web/samples/test-filament-viewer.html
+++ b/web/samples/test-filament-viewer.html
@@ -52,6 +52,15 @@ main {
This is a demonstration of the <filament-viewer> web component.
+No skybox, auto-expand, drag and drop
+
+
+
+
No skybox, auto-expand