本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
最近有个需求,需要本地上传3D模型到后端,并截图生成预览图。
思路
简单来书,上传模型时,用Babylon.js渲染模型,然后使用画布截图的功能。 但要注意的是,本地模型不一定就一个模型文件,还需要考虑模型的相关贴图和文件,比如说MTL,PNG等资源文件。
查看Babylon的文档和源码发现,Babylon的loader支持直接读取File
文件,FileInput类提供了加载FileList
功能,这个类会把批量上传的文件列表中的贴图资源,做一层Name-File
的映射,当Loader
读取到对应的贴图文件时,将会去读取对应的File
.
所以,我们需要实现一个FilePreview
类,这个类传入FileList
,然后解析FileList渲染到画布上,然后截图上传到后端,获取到预览图url后,在把url和模型一起发动到后端。
这篇文章仅实现前端截图部分.
实现
创建一个FilePreviewer类,先贴具体实现代码
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;
}
//模型缩放到全屏空间
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();
}
}
抛开初始化Babylon初始化场景不谈,这个类主要传入一个画布初始化类,通过loadLocalFile接口,接受file input
的event,开始解析文件列表,加载完成后把模型最大化的全屏空间,添加一个标记,在截图前判断是否模型已经被加载
今天的文章BabylonJs实现上传本地模型到后端生成预览图分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/19045.html