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

253 lines
7.2 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>
<canvas id="three"></canvas>
<div class="button" @click="toHomeView">
</div>
</div>
</template>
<script setup>
import * as THREE from "three";
import { ref, onMounted } from "vue";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
// 引入轨道控制器:支持鼠标左中右键操作和键盘方向键操作
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import {
CSS2DObject,
CSS2DRenderer,
} from "three/examples/jsm/renderers/CSS2DRenderer";
// import renderModel from "../renderModel";
import homeIcon from '@/assets/image/bg7.jpg'
import TWEEN from "@tweenjs/tween.js";
import bus from '@/utils/bus.js'
let scene = ref(null);
let renderer = ref(null);
let camera = ref(null);
// 控制器
let orbit = ref(null);
let group = ref(null);
let mouse = new THREE.Vector2();
let raycaster = new THREE.Raycaster();
let labelRenderer = new CSS2DRenderer(); //新建CSS2DRenderer
let positionObj = ref(null);
// 选中的模型
let selectedObjects = ref([]);
const props = defineProps({
background: { // 背景颜色
default: '#fff',
type: String
},
sceneUrl: { // 模型路径
default: '/jz/glb/scene.gltf',
type: String
},
light: {
default: '0xffffff',
type: String
}
})
const data = reactive({
reductionMaterial: null
});
const { reductionMaterial } = toRefs(data);
watch(() => props.sceneUrl, val => {
init();
loadSence();
});
onMounted(() => {
bus.on('foo', e => {
})
init();
loadSence();
// 启动动画
renderScene();
document.addEventListener("click", onMouseDown);
});
const init = () => {
scene = new THREE.Scene("#00ffff");
const texture = new THREE.TextureLoader().load(homeIcon);
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture
scene.environment = texture
const canvas = document.querySelector("#three");
var cubeLoader = new THREE.CubeTextureLoader();
// 创建一个渲染器并设置大小WebGLRenderer将会使用电脑显卡来渲染场景
renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true, preserveDrawingBuffer: true });
//曝光
renderer.toneMappingExposure = 2;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.25, 2000)
// 将摄像机对准场景的中心
camera.position.set(0, 104.545, -21.637);
// camera.lookAt(scene.position);
// 创建控件对象
orbit = new OrbitControls(camera, renderer.domElement);
orbit.autoRotate = true;
setTimeout(function () {
orbit.autoRotate = false;
}, 7000);
renderer.setClearColor(new THREE.Color("#0e0934"));
renderer.setSize(window.innerWidth, window.innerHeight);
// 环境光 光源颜色从0x444444更改为0x888888,可以看到threejs场景中的网格模型表面变的更亮。
// intensity 强度属性0.5
var alight = new THREE.AmbientLight(props.light, 0.5);
alight.name = "aLight";
scene.add(alight);
};
const loadSence = () => {
const gltfLoader = new GLTFLoader();
gltfLoader.load(props.sceneUrl, (gltf) => {
var model = gltf.scene;
scene.add(model);
});
};
const renderScene = () => {
orbit.update(); // 拖动
// 使用requestAnimationFrame函数进行渲染
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
// 写在requestAnimationFrame之后,否则会报错
labelRenderer.render(scene, camera);
};
// 相机移动动画
const initTween = (targetX, targetY, targetZ) => {
let initPosition = {
x: camera.position.x,
y: camera.position.y,
z: camera.position.z,
};
let tween = new TWEEN.Tween(initPosition)
.to({ x: targetX, y: targetY, z: targetZ }, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut);
let onUpdate = (pos) => {
let x = pos.x;
let y = pos.y;
let z = pos.z;
if (pos.z < 0) {
camera.position.set(x, y, z - 12);
} else {
camera.position.set(x, y, z + 12);
}
};
tween.onUpdate(onUpdate);
tween.start();
if (positionObj.z < 0) {
orbit.target.set(
positionObj.x,
positionObj.y - 0.4,
-12
);
} else {
orbit.target.set(
positionObj.x,
positionObj.y - 0.4,
12
);
}
}
// 储存被选中的模型和材质
let selectedObject = null;
let selectedMaterial = null;
const onMouseDown = (event) => {
var raycaster = new THREE.Raycaster()
// 恢复之前被选中模型的材质
if (selectedObject && selectedMaterial) {
selectedObject.material = selectedMaterial;
selectedObject = null;
selectedMaterial = null;
}
// 计算鼠标点击位置的归一化设备坐标
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 更新射线的起点和方向
raycaster.setFromCamera(mouse, camera);
// 计算射线和场景中所有可点击物体的相交情况
const intersects = raycaster.intersectObjects(scene.children, true);
// 如果找到了模型,将其材质修改为绿色
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
// 储存被选中的模型和材质
selectedObject = clickedObject;
selectedMaterial = clickedObject.material;
// 修改材质为绿色
selectedObject.material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
}
// 拉近场景
if (!intersects[0]) {
return;
} else {
if (intersects[0].object.name.indexOf('box') > -1) {
selectedObjects = [];
selectedObjects.push(intersects[0].object);
positionObj = {
x: intersects[0].object.position.x,
y: intersects[0].object.position.y,
z: intersects[0].object.position.z,
};
initTween(
positionObj.x,
positionObj.y,
positionObj.z
);
}
}
}
// 返回主页
const toHomeView = () => {
let initPosition = {
x: camera.position.x,
y: camera.position.y,
z: camera.position.z,
};
let tween = new TWEEN.Tween(initPosition)
.to({ x: 0, y: -(5 * 24) / 12, z: (6 * 100) / 5 }, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut);
let onUpdate = (pos) => {
let x = pos.x;
let y = pos.y;
let z = pos.z;
camera.position.set(x, y, z);
};
tween.onUpdate(onUpdate);
tween.start();
orbit.target.set(0, 0, 0);
}
</script>
<style lang='scss' scoped>
#three {
height: 100%;
width: 100%;
}
.button {
width: 80px;
height: 80px;
line-height: 80px;
border-radius: 50%;
border: 1px solid rgb(7, 207, 221);
position: absolute;
z-index: 1000000;
bottom: 15px;
left: 10px;
color: #fff;
text-align: center;
background: rgb(7, 207, 221);
cursor: pointer;
}
</style>