bw/src/views/bim/bimHome/components/ThreeView2.vue

487 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div>
<div id="gltf"></div>
<div id="dom"></div>
<div class="btnGroup">
<div class="button" @click="toHomeView">
</div>
<div class="button" @click="setLabel">
{{ isAddLabel ? '' : '' }}
</div>
<div class="button" @click="setBottom()">
{{ isRemoveBottom ? '' : '' }}
</div>
<div class="button" @click="roam()">
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue';
import * as THREE from 'three';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import TWEEN from "@tweenjs/tween.js";
import {
CSS2DObject,
CSS2DRenderer,
} from "three/examples/jsm/renderers/CSS2DRenderer";
import Bus from '@/utils/bus.js'
import bimStore from '@/store/modules/bim';
import gsap from "gsap";
onMounted(() => {
init();
loadSence();
renderScene();
//点击模型
document.addEventListener('click', onMouseDown);
// 右击事件
window.addEventListener('contextmenu', function (e) {
if (e.button === 2) {
onMouseDownRight(e);
}
});
// document.addEventListener("mousemove", onMouseMove);
});
let scene, renderer, camera, controls, pointsArr = true;
let biggerSphereMesh = null;
let i = 1;
let mouse = new THREE.Vector2();
let labelRenderer = new CSS2DRenderer(); //新建CSS2DRenderer
const data = reactive({
isAddLabel: true,
isRemoveBottom: true
});
const { isAddLabel, isRemoveBottom } = toRefs(data);
// 建筑树点击
Bus.on('clickBuild', (isParent) => {
// Todo
console.log('clickBuild', isParent);
if (!isParent) {// 点击子级
var clickName = bimStore().activateTree.clickName;
var Floor = scene.getObjectByName(clickName);
//投放间坐标 x: 42.6454163 y: 5.36651754 z: -95.03277
//糖化间坐标 x: 27.127655 y: 8.55 z: -6.132766
//原料处理间坐标 x: 27.6954441 y: 10.7530384 z: -37.73485
// 拉近场景
// nearCamera(Floor);
boxLight(Floor);
return;
}
})
// 设备树点击
Bus.on('clickDevice', (isParent) => {
if (!isParent) {// 点击子级
var clickName = bimStore().activateDevice.clickName;
var Floor = scene.getObjectByName(clickName);
boxLight(Floor);
// 拉近距离
// nearCamera(Floor);
return;
}
})
// 系统树点击
Bus.on('clickApplication', e => {
// Todo
console.log('clickApplication');
})
watch(() => bimStore().activateMenu, value => {
// 更换场景颜色
scene.background = new THREE.Color(value.background);
toHomeView();
}, { deep: true });
const init = () => {
// 场景
scene = new THREE.Scene();
scene.background = new THREE.Color(bimStore().activateMenu.background);
// 相机
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(-92.650, 67.456, 38.088);
//渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding;
document.getElementById('gltf').appendChild(renderer.domElement);
controls = new OrbitControls(camera, renderer.domElement);
const pmremGenerator = new THREE.PMREMGenerator(renderer);
scene.environment = pmremGenerator.fromScene(new RoomEnvironment()).texture;
}
const loadSence = () => {
const gltfLoader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/gltf/')//设置解压库文件路径
gltfLoader.setDRACOLoader(dracoLoader)
gltfLoader.load('/jzc/scene.gltf', (gltf) => {
var model = gltf.scene;
roamdObjects = model.children[0].children;
scene.add(model);
});
};
const renderScene = () => {
requestAnimationFrame(renderScene);
TWEEN.update();
controls.update();
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
};
const onWindowResize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener("resize", onWindowResize, false);
// 能选中的组
const enableGroup = [
'set1',
'set2',
'set3',
'set4',
'set5',
'set6',
'set7',
'set8',
'yuanliao_room',
'tanghua_room',
'touliao_room',
]
const isSelent = (obj) => {
var o = obj
while (true) {
if (o.name != "jzgltf" && enableGroup.indexOf(o.name) != -1) {
return o;
} else {
if (o.parent.name == "jzgltf") {
return null;
} else {
o = o.parent;
}
}
}
}
let selectedObjects = ref([]);
let selectBoxByClick, selectBoxByMouseon, pointLabel;
var marginLeft = 0;
var marginTop = 70;
// 鼠标移动效果
const onMouseMove = (event) => {
var raycaster = new THREE.Raycaster()
mouse.x = ((event.clientX - marginLeft) / window.innerWidth) * 2 - 1;
mouse.y = -((event.clientY - marginTop) / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
var selectedObject = isSelent(clickedObject);
if (selectedObject) {
selectedObjects[0] = selectedObject;
if (selectBoxByMouseon) {
scene.remove(selectBoxByMouseon);
}
selectBoxByMouseon = new THREE.BoxHelper(selectedObjects[0], '#ffffff');
scene.add(selectBoxByMouseon);
}
}
}
const emit = defineEmits(['handleRightClick'])
// 鼠标右击事件
const onMouseDownRight = (event) => {
var raycaster = new THREE.Raycaster()
mouse.x = ((event.clientX - marginLeft) / window.innerWidth) * 2 - 1;
mouse.y = -((event.clientY - marginTop) / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
console.log(123, event);
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
console.log('这是我选中的模型', clickedObject);
var selectedObject = isSelent(clickedObject);
emit('handleRightClick', selectedObject);
}
}
// 鼠标点击效果
const onMouseDown = (event) => {
var raycaster = new THREE.Raycaster()
mouse.x = ((event.clientX - marginLeft) / window.innerWidth) * 2 - 1;
mouse.y = -((event.clientY - marginTop) / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
console.log(123, event);
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
console.log('这是我选中的模型', clickedObject);
var selectedObject = isSelent(clickedObject);
// 高亮所在区域
boxLight(selectedObject);
if(bimStore().activateIndex == '0') {
// 联动左侧菜单
Bus.emit('handleTreeClick', clickedObject);
return;
}
if(bimStore().activateIndex == '1') {
// 联动左侧菜单
Bus.emit('handleTreeClick1', clickedObject);
return;
}
}
}
const boxLight = (selectedObject) => {
if (selectedObject) {
selectedObjects[0] = selectedObject;
if (selectBoxByClick) {
scene.remove(selectBoxByClick);
}
selectBoxByClick = new THREE.BoxHelper(selectedObjects[0], '#00ffff');
scene.add(selectBoxByClick);
centerSelectedGroup(selectedObject);
}
}
// 居中显示选中
const centerSelectedGroup = (obj) => {
let tween = new TWEEN.Tween(controls.target).to(obj.position, 1000).easing(TWEEN.Easing.Quadratic.Out);
controls.enabled = false;
tween.onComplete(function () {
controls.enabled = true;
});
tween.start();
}
// 漫游
const roam = () => {
//漫游坐标
const path = new THREE.CatmullRomCurve3([
new THREE.Vector3(130.076658411178222, 58.489999368786826, -32.514521795452985), // 起始坐标
new THREE.Vector3(128.938067229663826, 55.0220540878815, 30.184599697589874) //中间节点
]);
d(path)
pointsArr = path.getSpacedPoints(50);
i = 0;
console.log(pointsArr[i]);
// 设置相机起点, 相机位置曲线上当前点pointsArr[i]
camera.position.copy(pointsArr[i]);
// 相机观察目标当前点的下一个点pointsArr[i + 1]
camera.lookAt(pointsArr[i + 1]);
// controls.target.copy(pointsArr[i+1])
setTimeout(() => {
renders();
}, 1000);
}
const d = (cameraCurve) => {
let sphereCurve = null;
//参考路径上取100个点每个点上添加蓝色小球
//做一个小0.6倍的路径添加到场景,作相机运动路径参考
sphereCurve = cameraCurve.clone()
sphereCurve.points.forEach(point => {
point.x *= 0.6
point.y *= 0.2
point.z *= 0.6
return point
})
//参考路径上取100个点每个点上添加蓝色小球
const pathPoints = sphereCurve.getPoints(100)
pathPoints.forEach(point => {
const sphere = new THREE.SphereGeometry(0.2)
const sphereMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff
})
const sphereMesh = new THREE.Mesh(sphere, sphereMaterial)
sphereMesh.position.set(point.x, point.y, point.z)
scene.add(sphereMesh)
})
//绘制一条红色的路径参考线
const geometry = new THREE.BufferGeometry().setFromPoints(pathPoints)
const material = new THREE.LineBasicMaterial({
color: 0xff0000
})
const curveObject = new THREE.Line(geometry, material)
scene.add(curveObject)
//做一个较大的绿色小球沿相机相反反向移动
const biggerSphere = new THREE.SphereGeometry(2)
const sphereMaterial1 = new THREE.MeshBasicMaterial({ color: 0x0ff00 })
biggerSphereMesh = new THREE.Mesh(biggerSphere, sphereMaterial1)
biggerSphereMesh.position.set(
pathPoints[0].x,
pathPoints[0].y,
pathPoints[0].z
)
scene.add(biggerSphereMesh)
}
let num = 1;
const renders = () => {
/* angle += 0.01;
// 相机y坐标不变在XOZ平面上做圆周运动
camera.position.x = R * Math.sin(angle);
camera.lookAt(0, 0, 0); */
if (num < pointsArr.length - 1) {
// 相机位置设置在当前点位置
camera.position.copy(pointsArr[num]);
// camera.position.set(pointsArr[num]);
// 曲线上当前点pointsArr[num]和下一个点pointsArr[num+1]近似模拟当前点曲线切线
// 设置相机观察点为当前点的下一个点,相机视线和当前点曲线切线重合
camera.lookAt(pointsArr[num + 1]);
biggerSphereMesh.position.copy(pointsArr[num]
)
// controls.target.copy(pointsArr[num+1])
num += 1; //调节速度
requestAnimationFrame(renders);
controls.update();
} else {
num = 0
}
};
let roamdObjects = ref([]);
// 返回初始值
const toHomeView = () => {
if (selectBoxByClick) {
scene.remove(selectBoxByClick);
}
removeLabel();
controls.reset();
}
// 拉近距离
const nearCamera = (floor) => {
controls.reset();
gsap.to(camera.position, {
x: floor.position.x,
y: floor.position.y,
z: floor.position.z,
duration: 2,
ease: "power1.inOut",
onComplete: () => {
},
});
}
//标签
const setLabel = () => {
if (isAddLabel.value) {
addLabel();
} else {
removeLabel();
toHomeView();
}
isAddLabel.value = !isAddLabel.value;
}
// 添加标签
const addLabel = () => {
let obj = scene.getObjectByName('set2');
// console.log(123, obj);
centerSelectedGroup(obj);
let text = "设备二";
pointLabel = createLableObj(text);
obj.add(pointLabel);
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = "absolute";
labelRenderer.domElement.style.top = 0;
labelRenderer.domElement.style.pointerEvents = 'none';// 必须加上
document.body.appendChild(labelRenderer.domElement);
// 将呈现器的输出添加到HTML元素
document.getElementById("dom").appendChild(renderer.domElement);
};
const createLableObj = (text) => {
let laberDiv = document.createElement("div"); //创建div容器
laberDiv.className = "laber_name";
laberDiv.innerHTML = `<div class="arrow_outer"></div><span>设备名称:${text}</span><span>状态:启用</span><span>压力50 Pa</span>`
let pointLabel = new CSS2DObject(laberDiv);
return pointLabel;
};
const removeLabel = () => {
if(pointLabel) {
isAddLabel.value = true;
document.body.removeChild(labelRenderer.domElement);
}
}
// 设置地板
const setBottom = () => {
var target = scene.getObjectByName('floor2F');
console.log(target);
if (isRemoveBottom.value) {
target.visible = false;
} else {
target.visible = true;
}
isRemoveBottom.value = !isRemoveBottom.value;
}
</script>
<style lang='scss'>
#gltf {
height: 100%;
width: 100%;
}
.btnGroup {
width: 180px;
position: absolute;
left: 260px;
top: 10px;
z-index: 999;
display: flex;
flex-direction: column;
justify-content: space-between;
color: #ffff;
>div {
cursor: pointer;
// background: #0549a7;
border: 1px solid #3cbfdf;
width: 110px;
height: 40px;
border-radius: 5px;
line-height: 40px;
text-align: center;
margin-top: 10px;
}
}
.laber_name {
width: 120px;
height: 90px;
padding: 5px 10px;
white-space: nowrap;
background-color: #ffffffb3;
border-radius: 12px;
font-size: 12px;
display: flex;
flex-direction: column;
span {
line-height: 24px;
}
}
</style>