This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.

preface

Recently, there was a requirement to upload 3D model locally to the back end and generate preview images with screenshots.

Train of thought

When uploading the model, render the model with Babiel.js and then use the canvas capture function. However, it is important to note that the local model does not have to be a model file. You also need to consider the relevant maps and files of the model, such as MTL, PNG and other resource files.

Check the files and source code of Babylon. The Loader of Babylon supports directly reading File files. The FileInput class provides the FileList function. When Loader reads the corresponding map File, it will read the corresponding File.

So, we need to implement a FilePreview class, which passes in a FileList, parses the FileList and renders it on the canvas, uploates the screenshot to the back end, gets the PREVIEW URL, and then executes the URL along with the model to the back end.

This article only implements the front-end screenshot part.

implementation

Create a FilePreviewer class and paste the implementation code first

import { ArcRotateCamera, Color3, Engine, FilesInput, FramingBehavior, Observable, Scene, StandardMaterial, Tools, } from "@babylonjs/core"; export class FilePreviewer { private filesInput: FilesInput; private engine: Engine; private scene: Scene; private onFileLoadedObserver = new Observable<File>(); private isRendered = false; constructor(canvas: HTMLCanvasElement) { this.engine = new Engine(canvas, false, { premultipliedAlpha: false, preserveDrawingBuffer: true, antialias: false, }); this.scene = new Scene(this.engine); this.scene.createDefaultCameraOrLight(); this.scene.render(); let filesInput = new FilesInput( this.engine, null, (sceneFile, scene) => { this.scene = scene; const isGLTF = sceneFile.name.endsWith(".glb") || sceneFile.name.endsWith(".gltf"); this.prepareCamera(isGLTF); (scene.materials as StandardMaterial[]).forEach((m) => { m.diffuseColor = Color3.White(); m.backFaceCulling = false; m.emissiveColor = Color3.Black(); }); this.onFileLoadedObserver.notifyObservers(sceneFile); }, (e) => {}, () => { this.isRendered = true; }, (r) => {}, () => {}, null, () => {} ); this.filesInput = filesInput; } / / model zoom to full screen space prepareCamera (isGLTF = false) {this. Scene. CreateDefaultCameraOrLight (true, true); const camera = this.scene.activeCamera as ArcRotateCamera; if (isGLTF) camera.alpha += Math.PI; camera.useFramingBehavior = true; const framingBehavior = camera.getBehaviorByName( "Framing" ) as FramingBehavior; 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); } camera.pinchPrecision = 200 / camera.radius; camera.upperRadiusLimit = 5 * camera.radius; Camera. WheelDeltaPercentage = 0.01; Camera. PinchDeltaPercentage = 0.01; camera.attachControl(); } loadLocalFile(evt: any) { this.isRendered = false; this.filesInput.loadFiles(evt); } async getPreviewUrl(): Promise<string> { await this.waitRendered(); return new Promise((res) => { Tools.CreateScreenshot( this.engine, this.scene.activeCamera! , 400, (data) => { res(data); }); }); } async waitRendered() { return new Promise((res) => { setInterval(() => { if (this.isRendered) res(null); }, 100); }); } destory() { this.filesInput.dispose(); this.onFileLoadedObserver.clear(); this.engine.dispose(); }}Copy the code

Instead of initializing the Babylon initialization scene, this class mainly passes in a canvas initializer class that, via the loadLocalFile interface, accepts the event of file input, starts parsing the file list, and after loading maximizes the model’s full-screen space, adds a tag, Determine if the model has been loaded before taking a screenshot

The source address