前言
项目中通过添加文件后,将stl格式的模型文件加载到场景中,后续发现stl格式文件太大,普遍是十几兆,最后研究出将stl格式文件转为drc格式文件加载,场景加载模型的过程只需要几秒即可,接上来上代码
// 整个项目是vue3+js的项目,通过vite进行打包,使用的pinia来替代的vuex,因为vuex已经很久没更新最新版本了,pinia是尤大神推荐的库,小伙伴可以了解下,使用方法类似但是更为简便
// handleChange为input框中file类型的change事件,当选择文件后就会触发该change事件
handleChange(file) {
const uuid = info.uuid // 此uuid是跟随的info,由后端提供
const mark = nanoid(1) // 此nanoid是取代hat的一个获取随机字符串的库,只有几kb大小
// filePathTransform方法外部封装,返回的path为url名称,data为blob二进制格式的内容
const { path, data } = await filePathTransform(file, uuid, mark)
console.log(path, data)
}
// 接下来是封装的工具库,内部是基于threejs的
import * as THREE from 'three'
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { DRACOExporter } from 'three/examples/jsm/exporters/DRACOExporter'
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
const lOADTYPE = {
UNKNOWN: 0,
DRC: 1,
STL: 2,
PLY: 3,
MQ: 4,
values: {
0: { name: 'unknown', value: 0 },
1: { name: 'drc', value: 1 },
2: { name: 'stl', value: 2 },
3: { name: 'ply', value: 3 },
4: { name: 'mq', value: 4 },
},
}
export async function filePathTransform(file, uuid, mark) {
let path = ''
let data = null
const { name } = file
const type = name.slice(name.lastIndexOf('.') + 1)
// 通过type的文件后缀名判断类型,如果是drc的则直接转换,否则比如stl的则需要将data转为blob二进制类型
if (type !== 'drc') {
path = `${mark}/${uuid}/${mark}.drc`
data = await transformBlob(file)
} else {
path = `${mark}/${uuid}/${mark}${type}`
data = file
}
return { path, data }
}
// 通过DRACOExporter来进行file的文件压缩,注意要使用DRACOExporter的话,需要引入文件在public/libs下引入draco文件夹
//里面内置一些decoder的js文件,具体文件可以参照[添加链接描述](https://threejs.org/docs/index.html?q=DRA#examples/zh/loaders/DRACOLoader)
// 引入完文件夹后需要在vue根目录下的index.html中<script src="/libs/draco/draco_encoder.js"></script>引入好刚刚的文件,才可以正常使用decoder,否则会报错缺少deoderModules模块
// 这里的操作就将stl格式的文件通过draco压缩成了blob二进制格式的文件,loadModel的作用是获取该文件导出的mesh网格对象
export async function transformBlob(file) {
const exporter = new DRACOExporter()
const mesh = await loadModel(file)
const result = exporter.parse(mesh, {
exportColor: false,
exportUvs: false,
})
const blob = new Blob([result], { type: 'application/octet-stream' })
return blob
}
// 创建通过file创建的mesh网格对象
// URL.createObjectURL():静态方法会创建一个DOMSring,其中包含一个表示参数中给出的对象的URL。这个URL的生命周期和创建它窗口的document绑定,这个URL对象表示指定的File对象或Blob对象
export function loadModel(file) {
let name = file
let path = file
if (file instanceof Blob) {
name = file.name
path = URL.createObjectURL(file)
}
// 通过文件name值获取到文件类型
const loaderType = getFileType(name)
// 通过文件类型获取到对应的loader文件加载器
const loader = setLoaderType(loaderType)
return new Promise((resolve) => {
loader.load(path, (geometry) => {
const material = new THREE.MeshStandardMaterial({
side: THREE.DoubleSide,
color: 0xffffff,
})
if (!geometry.isBufferGeometry) {
const loaderGeometry = new THREE.BufferGeometry()
loaderGeometry.fromGeometry(geometry)
geometry = loaderGeometry
}
geometry.computeVertexNormals()
const mesh = new THREE.Mesh(geometry, material)
mesh.url = file
resolve(mesh)
})
})
}
export function getFileType(name) {
for (const typeIndex in lOADTYPE.values) {
const typeName = lOADTYPE.values[typeIndex].name
const str1 = `.${typeName}`
const str2 = `.${typeName}?`
if (name.endsWith(str1) || name.indexOf(str2) > 0) {
return lOADTYPE.values[typeIndex].value
}
}
return lOADTYPE.UNKNOWN
}
export function setLoaderType(loadType) {
let loader = null
switch (loadType) {
case lOADTYPE.DRC:
case lOADTYPE.MQ:
// DRACOLoader使用也需要引入libs的draco文件
loader = new DRACOLoader()
loader.setDecoderPath('/libs/draco/')
loader.setDecoderConfig({ type: 'js' })
break
case lOADTYPE.STL:
loader = new STLLoader()
break
case lOADTYPE.PLY:
loader = new PLYLoader()
break
default:
loader = null
}
return loader
}
通过以上步骤完成的path就是转换好的drc文件名称,data就是转换好的二进制blob数据格式,只需要将期一起传入服务器,然后取得其服务器的网址就能使用drc格式的图片,大小一般都从十几兆变为了几KB