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
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>
|