You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

324 lines
8.0 KiB
Vue

<template>
<div class="scene-wrapper">
<div ref="containerRef" class="container" />
<!-- 固定位置的控制按钮 -->
<div class="control-buttons">
<el-button type="primary" @click="displayModel('group_sd')" :icon="Bell" />
<el-button type="danger" @click="displayModel('group_voice')" :icon="MagicStick" />
<el-button type="warning" @click="displayModel('group_manual')" :icon="Star" />
<el-button type="warning" @click="displayModel('all')" :icon="Compass" />
<el-button type="warning" @click="camera.position.set(-50, 45, -140)" :icon="Refresh" />
</div>
</div>
</template>
<script lang="ts" setup>
import {
Check,
Delete,
Edit,
Message,
Search,
Star,
Bell,
MagicStick,
Refresh,
Compass
} from '@element-plus/icons-vue';
import { onMounted, onUnmounted, ref, nextTick } from 'vue';
import {
AxesHelper,
Color,
PerspectiveCamera,
Scene,
WebGLRenderer,
AmbientLight,
DirectionalLight,
PointLight,
MeshStandardMaterial,
Vector2,
Raycaster,
CanvasTexture,
SpriteMaterial,
Sprite,
Box3,
Vector3,
HemisphereLight,
Mesh
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
const containerRef = ref<HTMLDivElement>();
const conditionTagRef = ref<any>();
const showTooltip = ref(false);
// 场景
const scene = new Scene();
// 创建摄像机
const camera = new PerspectiveCamera(
45,
containerRef.value?.clientWidth! / containerRef.value?.clientHeight!,
0.1,
1000
);
camera.position.set(-50, 45, -140);
camera.lookAt(scene.position);
const axesHelper = new AxesHelper(100);
scene.add(axesHelper);
// 渲染器
const renderer = new WebGLRenderer({
antialias: true, // 启用抗锯齿
alpha: true, // 启用透明背景
preserveDrawingBuffer: true // 保持绘图缓冲区
});
renderer.setClearColor(new Color('#131519'));
renderer.setSize(containerRef.value?.clientWidth!, containerRef.value?.clientHeight!);
renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio || 1); // 设置像素比率
// 轨道摄像机
const orbitControls = new OrbitControls(camera, renderer.domElement);
orbitControls.autoRotate = false;
orbitControls.autoRotateSpeed = 5;
orbitControls.dampingFactor = 0.1;
orbitControls.enableDamping = true;
orbitControls.enableRotate = true;
orbitControls.minDistance = 10;
orbitControls.maxPolarAngle = Math.PI * 0.5;
// 物体
scene.add(new AxesHelper(20));
// 环境光:增强整体亮度
const ambientLight = new AmbientLight('#ffffff', 1.8); // 强度从 0.6 提高到 1.0
scene.add(ambientLight);
// 平行光
const directionalLight = new DirectionalLight('#ffffff', 1.8); // 强度从 0.2 提高到 0.8
scene.add(directionalLight);
directionalLight.position.set(20, 20, 10);
// // 点光源
const pointLight = new PointLight('#ffffff', 1.8, 1800); // 强度从 0.5 提高到 1.0
scene.add(pointLight);
pointLight.position.set(0, 40, 0);
// 创建一个悬浮的框
// const ctx = canvas.getContext('2d');
// if (ctx) {
// ctx.fillStyle = 'rgba(0,0,0,.7)';
// ctx.fillRect(0, 0, 200, 100);
// ctx.fillStyle = 'white';
// ctx.font = '21px 黑体';
// ctx.textAlign = 'center';
// ctx.textBaseline = 'middle';
// }
// const scaleFactor = 5;
// const texture = new CanvasTexture(canvas);
// const material = new SpriteMaterial({ map: texture });
// const sprite1 = new Sprite(material);
// sprite1.scale.set(scaleFactor, scaleFactor, scaleFactor);
// sprite1.position.set(-9.644502679789724, 5.815449539931501 + 0.5, 21.66345220492572);
function animate() {
requestAnimationFrame(animate);
orbitControls.update();
renderer.render(scene, camera);
}
let pitchObj = ref<any>();
let pitchMaterial = ref<any>();
// ResizeObserver 监听容器尺寸变化
let resizeObserver: ResizeObserver | null = null;
let groupSdArrs: any[] = [];
let groupVoiceArrs: any[] = [];
let groupManualArrs: any[] = [];
function displayModel(modelName: string) {
if (modelName === 'group_sd') {
groupSdArrs.forEach((obj) => {
obj.visible = true;
});
groupVoiceArrs.forEach((obj) => {
obj.visible = false;
});
groupManualArrs.forEach((obj) => {
obj.visible = false;
});
} else if (modelName === 'group_voice') {
groupVoiceArrs.forEach((obj) => {
obj.visible = true;
});
groupSdArrs.forEach((obj) => {
obj.visible = false;
});
groupManualArrs.forEach((obj) => {
obj.visible = false;
});
} else if (modelName === 'group_manual') {
groupManualArrs.forEach((obj) => {
obj.visible = true;
});
groupVoiceArrs.forEach((obj) => {
obj.visible = false;
});
groupSdArrs.forEach((obj) => {
obj.visible = false;
});
} else if (modelName === 'all') {
groupSdArrs.forEach((obj) => {
obj.visible = true;
});
groupVoiceArrs.forEach((obj) => {
obj.visible = true;
});
groupManualArrs.forEach((obj) => {
obj.visible = true;
});
}
}
onMounted(() => {
const container = containerRef.value!;
container.appendChild(renderer.domElement);
// 初始化摄像机和渲染器尺寸
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
// 监听容器尺寸变化
resizeObserver = new ResizeObserver(() => {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
});
resizeObserver.observe(container);
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
loader.setDRACOLoader(dracoLoader);
//public\models\scene\test.glb
loader.load('/models/scene/test.glb', (gltf: any) => {
console.log(gltf.scene.children);
gltf.scene.scale.set(1, 1, 1);
scene.add(gltf.scene);
});
// public\models\texture\alpha01.png
// 创建射线检测器
const raycaster = new Raycaster();
const mouse = new Vector2();
// 添加点击事件监听
renderer.domElement.addEventListener('mousedown', (event: MouseEvent) => {
const rect = container.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
mouse.x = (x / container.clientWidth) * 2 - 1;
mouse.y = -(y / container.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
});
animate();
});
onUnmounted(() => {
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
});
</script>
<style scoped>
.scene-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.container {
width: 100%;
height: 100%;
position: relative;
}
.control-buttons {
position: absolute;
top: 25%;
left: 2%;
display: flex;
flex-direction: column;
gap: 20px;
z-index: 100;
align-items: flex-start; /* 确保所有按钮左对齐 */
}
/* Element Plus圆形按钮样式调整 */
.control-buttons :deep(.el-button) {
width: 40px !important;
height: 40px !important;
min-width: 40px !important;
padding: 0 !important;
display: flex !important;
align-items: center !important; /* 垂直居中 */
margin: 0 !important;
/* 确保所有按钮尺寸一致 */
box-sizing: border-box !important;
}
.control-btn {
padding: 8px 16px;
background: rgba(0, 0, 0, 0.7);
color: white;
border: 1px solid #4fc3f7;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
min-width: 80px;
}
/* .control-btn:hover {
background: rgba(79, 195, 247, 0.3);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.control-btn:active {
transform: translateY(0);
} */
/* 响应式调整 */
/* @media (max-width: 768px) {
.control-buttons {
top: 10px;
right: 10px;
gap: 8px;
}
.control-btn {
padding: 6px 12px;
font-size: 12px;
min-width: 70px;
}
} */
</style>