闫增涛
2025-06-12 e2952ba4b6a392047ddbc3542465a81413c64eb9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<template>
  <div class="showModel" v-loading="loading">
    <div :id="'container' + props.md5 + props.index" class="viewer-container" v-if="props.md5"></div>
    <el-empty v-else description="暂无数据" image-size="80" />
  </div>
</template>
 
<script setup lang="ts">
import { ref, onMounted, defineProps,inject } from "vue";
import * as THREE from "three";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
const props = defineProps<{md5:string,index:number}>();
const config: any = inject("config");
const loading = ref<boolean>(false);
onMounted(() => {
  loading.value = true;
  if(props.md5) {
     initThree();
  } else {
    loading.value = false;
  }
});
const initThree = () => {
  const container = document.getElementById("container" + props.md5 + props.index);
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(
    75,
    container.clientWidth / container.clientHeight,
    0.1,
    10000 // 增大远平面距离
  );
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(container.clientWidth, container.clientHeight);
  container.appendChild(renderer.domElement);
 
  // 添加光源
  const light = new THREE.DirectionalLight(0xffffff, 2);
  light.position.set(5, 10, 7.5);
  scene.add(light);
 
  // 加载模型
  const loader = new FBXLoader();
  const token = localStorage.getItem(config.tokenKey);
  loader.load(
    config.requestCtx +
      "/file/FileDownload/Download?md5=" +
      props.md5 +
      "&token=" +
      token,
    (object) => {
      scene.add(object);
 
      // 创建控件
      const controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.25;
      controls.screenSpacePanning = false;
 
      // 1. 计算模型尺寸和中心点
      const box = new THREE.Box3().setFromObject(object);
      const center = box.getCenter(new THREE.Vector3());
      const size = box.getSize(new THREE.Vector3());
 
      // 2. 计算合适的相机距离
      const maxDim = Math.max(size.x, size.y, size.z);
      const fov = camera.fov * (Math.PI / 180);
      let cameraDistance = Math.abs(maxDim / (2 * Math.tan(fov / 2)));
 
      // 3. 设置相机位置和方向
      camera.position.copy(center);
      camera.position.z += cameraDistance * 1.5; // 增加50%的额外空间
      camera.lookAt(center);
 
      // 4. 更新控件目标点
      controls.target.copy(center);
      controls.update();
 
      // 5. 调整相机剪裁平面
      const minDistance = cameraDistance * 0.1;
      const maxDistance = cameraDistance * 10;
      camera.near = minDistance > 0.1 ? minDistance : 0.1;
      camera.far = maxDistance < 10000 ? maxDistance : 10000;
      camera.updateProjectionMatrix();
 
      function animate() {
        requestAnimationFrame(animate);
        controls.update();
        renderer.render(scene, camera);
      }
      animate();
      loading.value = false;
    },
    undefined,
    (error) => {
      console.error("加载模型失败:", error);
      loading.value = false;
    }
  );
};
</script>
 
<style lang="less" scoped>
.showModel,
.viewer-container {
  width: 100%;
  height: 100%;
}
</style>