Three.js加载多模型,且导出为一个

前序

刚写完个新需求,记录一下:读取本地Excel文件,根据文件内信息,请求模型库模型,最后将加载的全部模型导出为一个整体。逻辑较为简单,主要是导出功能,第一次接触

实现思路

  1. 读取本地Excel
  2. 遍历Excel数组
  3. 全部加载完毕后,导出一个整体

代码步骤

读取本地Excel
import * as XLSX from 'xlsx';

// 前端文件导入解析成传递给服务器的数据
export let resolveFile = (event) => {
  return new Promise((resolve, reject) => {

    const file = event.target.files[0];
    const reader = new FileReader();

    reader.onload = (e) => {
      const data = new Uint8Array(e.target.result);
      const workbook = XLSX.read(data, { type: 'array' });
      const sheetName = workbook.SheetNames[0];
      const worksheet = workbook.Sheets[sheetName];
      const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });

      // Assuming the first row contains headers
      const headers = jsonData[0];
      const dataRows = jsonData.slice(1);

      // Convert data to objects with headers as keys
      const formattedData = dataRows.map(row => {
        const obj = {};
        headers.forEach((header, i) => {
          obj[header] = row[i];
        });
        return obj;
      });

      resolve(formattedData);
    };

    reader.readAsArrayBuffer(file);
  })
}

这里注意的是, 《 * as XLSX》不使用这种引入方式,会有问题

 

遍历Excel数组

将Excel得到的数组传递给加载模型的模块

// 生成变电站
async JointSite() {
  // 获取模型承载器
  const container = document.querySelector('#JointModelContainer');
  // 实例化模型器皿
  this.jointModel = new JointModel(container);
  // 递归渲染
  this.renderWinery();

  if(this.excelData.length) {
    // 加载模型
    await this.jointModel.loadJointModel(this.excelData);
  }
},

 使用for循环,请求模型库

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import instance from '../../global';

async function loadModel(singleModel) {
  const loader = new GLTFLoader();

  return new Promise((resolve, reject) => {
    instance.get('api', {
      params: {
        filename: singleModel
      },
      responseType: "arraybuffer", // 转成二进制
    })
    .then((res) => {
      // console.log('res:', res);

      // 将二进制数据转换为Blob对象
      const blob = new Blob([res.data]);

      // 创建Blob URL
      const url = URL.createObjectURL(blob);

      loader.load(
        url,
        (gltf) => {
          resolve(gltf);
        },
        undefined,
        (error) => {
          console.log(`Error loading GLB file`);
          reject(error);
        }
      )
    })
    .catch((error) => {
      console.log(`1层捕捉到了请求API错误↑(检查MinIO中是否存在该模型:${singleModel})`);
      reject(error);
    })
  })
}

export { loadModel }

这里是循环以及处理每一个模型

for (let item = 0; item < staticSourceDatas.length; item++) {
    console.log(staticSourceDatas[item]);
    try {
      // 从模型库请求模型
      let gltf = await loadModel(staticSourceDatas[item].model_name);

      if(gltf) {
        scene.add(gltf?.scene);
        this.render();
      }
    }
}

以上逻辑正确的话,在页面中可以看到模型依次加载/渲染的效果

scene.add(gltf?.scene);  是关键,将请求到的模型放置到一个场景下。这里注意的是:scene必须是私有变量。

全部加载完毕后,导出gltf

导出主代码


import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';

function exportGLTF( input, callback ) {

  const gltfExporter = new GLTFExporter();

  const options = {
    trs: params.trs,
    onlyVisible: params.onlyVisible,
    binary: params.binary,
    maxTextureSize: params.maxTextureSize
  };

  gltfExporter.parse(
    input,
    function ( result ) {

      if ( result instanceof ArrayBuffer ) {

        saveArrayBuffer( result, 'scene.glb' );

      } else {

        const output = JSON.stringify( result, null, 2 );
        saveString( output, 'scene.gltf' );

      }

      if (callback) {
        callback(true); // 下载完成状态为 true
      }

    },
    function ( error ) {

      console.log( '文件导出解析过程中发生错误', error );

    },
    options
  );

}

const link = document.createElement( 'a' );
link.style.display = 'none';
document.body.appendChild( link );

function save( blob, filename ) {

  link.href = URL.createObjectURL( blob );
  link.download = filename;
  link.click();

  // URL.revokeObjectURL( link.href );

}

function saveString( text, filename ) {

  save( new Blob( [ text ], { type: 'text/plain' } ), filename );

}


function saveArrayBuffer( buffer, filename ) {

  save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );

}

const params = {
  trs: false,
  onlyVisible: true,
  binary: false,
  maxTextureSize: 4096,
};


export { exportGLTF }

在上面代码中,我将下载的进度使用回调函数返回去了,那么就可以拿到下载完成?的状态

exportGLB() {
// 导出GLB

  console.log('开始下载...');
  return new Promise((resolve, reject) => {
    exportGLTF(scene, (downState) => {
      resolve(downState);
    })
  })

}

ok,最后在vue文件给个下载完成的提示

// 导出GLB
exportFileGLB() {

  this.jointModel.exportGLB()
      .then((res) => {

        if(res) {
          this.$message.success('导出成功');
        } else {
          this.$message.success('导出失败');
        }
        
      })

},

这些threeJs官方,都有示例:

three.js/examples/misc_exporter_gltf.html at master · mrdoob/three.js · GitHub

感兴趣的小伙伴,可以看一看 文中提到的KTX2Loader 和 MeshoptDecoder

这两个都是用来优化的点。拜~

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值