<template>
|
<el-dialog
|
v-model="dialogVisible"
|
@open="openDialog"
|
destroy-on-close
|
class="prviewModel"
|
>
|
<div v-loading="loading">
|
<div
|
id="container"
|
class="viewer-container"
|
v-if="selectData.md5"
|
></div>
|
<el-empty v-else description="暂无数据" />
|
</div>
|
</el-dialog>
|
</template>
|
|
<script setup lang="ts">
|
import * as THREE from "three";
|
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
|
import { ref, defineExpose, inject } from "vue";
|
const config: any = inject("config");
|
const dialogVisible = ref<boolean>(false);
|
const selectData = ref<any>(null);
|
const loading = ref<boolean>(false);
|
const openDialog = () => {
|
loading.value = true;
|
if (selectData.value.md5) {
|
initThree();
|
} else {
|
loading.value = false
|
}
|
};
|
const initThree = () => {
|
const container = document.getElementById("container");
|
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=" +
|
selectData.value.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
|
},
|
|
);
|
};
|
const handleDialogVisible = (value: boolean, data: any) => {
|
dialogVisible.value = value;
|
selectData.value = data;
|
console.log("select", selectData.value);
|
};
|
defineExpose({ handleDialogVisible });
|
</script>
|
|
<style lang="less">
|
.prviewModel {
|
width: 60vw;
|
min-height: 580px;
|
.viewer-container {
|
width: 100%;
|
height: 60vh;
|
.el-empty {
|
margin-top: 100px !important;
|
}
|
}
|
}
|
</style>
|