Files
tunnel-cloud-web/src/components/content/tunnelScene/sceneClass/demo.js

568 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { handleLClick } from "./handleRLclick/handleLclick";
import { handleRClick } from "./handleRLclick/handleRclick";
import { handleLHover } from "./handleRLclick/handleLhover.js"
import { handleDBLClick } from "./handleDBLclick";
import { checkAnimation } from "./checkTag";
import {
handleEndChange,
handleStartChange,
} from "./handleOrbitControlsChange";
import { saveState, resetState } from "./viewBack";
import addFunction from "./addEvent";
import { editTunnelInit } from "./editTunnelInit";
import { addEquipment, removeEquipment } from "./editEquipment";
import previewtunnelModeInit from "./previewTunnelInit"
import { StreetSignTag } from "./utils/StreetSignTag/StreetSignTag.js";
// import { ref } from "vue";
export default class Demo {
// 摄像机看向位置s
origin = null;
//附着点的模型122
points = [];
//设备模型数组
deviceModels = [];
constructor(three, mountedElement) {
this._StreetSignTag = StreetSignTag
this._handleLClick = handleLClick;
this._handleLHover = handleLHover;
this._handleRClick = handleRClick;
this._handleDBLClick = handleDBLClick;
this._checkAnimation = checkAnimation;
this._saveState = saveState;
this._resetState = resetState;
this.addEquipment = addEquipment;
this.removeEquipment = removeEquipment;
this.previewtunnelModeInit = previewtunnelModeInit;
this.editTunnelInit = editTunnelInit;
// 外部可添加函数
this.addFunction = addFunction;
this.THREE = three;
this.mountedElement = mountedElement;
//初始化场景
this.scene = new this.THREE.Scene();
//初始化光影
this.ambientLight = new this.THREE.AmbientLight(0xffffff, 2); //设置环境光
// 初始化摄像机
this.camera = new this.THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
1,
1000
);
this.camera.position.z = 1.24;
this.camera.position.y = 9.14;
this.camera.position.x = -63.79;
this.camera.lookAt(0, 0, 1000);
// 初始化渲染器
this.renderer = new this.THREE.WebGLRenderer({
// 抗锯齿
antialias: true,
});
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.domElement.style.position = "absolute";
this.mountedElement.appendChild(this.renderer.domElement);
// 创建渲染帧
this.render = () => {
this.__renderScope();
requestAnimationFrame(this.render);
};
this.render();
this.canvasResize();
this.addXYZ();
this.addLight();
}
//渲染函数作用域(这里主要写渲染帧内操作)
__renderScope() {
this.renderer.render(this.scene, this.camera);
// 轨道控制器更新
if (this.orbitControls) {
this.orbitControls.update();
// console.log(this.camera.position);
}
//html标签渲染
if (this.CSS2Renderer) {
this.CSS2Renderer.render(this.scene, this.camera);
}
// 选中标签动画渲染
if (this.checkAnimationTween) {
this.checkAnimationTween.update();
}
// 视角重置动画
if (this.resetViewAngleAnimation) {
this.resetViewAngleAnimation.update();
}
//视角回退动画
if (this.viewSateResetAnimation) {
this.viewSateResetAnimation.update();
}
// 视角跟随动画
if (this.intoPointAnimation) {
this.intoPointAnimation.update();
}
// 隧道颜色渐变动画
if (this.opacityTween) {
this.opacityTween.update();
}
// 双击进入动画
if (this.dblIntoTween) {
this.dblIntoTween.update();
}
// 风机叶片旋转动画
if (this.fanSpinArray && this.fanSpinArray.length) {
this.fanSpinArray.forEach((element) => element.update());
}
}
// 添加世界坐标系
addXYZ() {
// const axesHelper = new this.THREE.AxesHelper(100);
// this.scene.add(axesHelper);
}
addLight() {
this.scene.add(this.ambientLight); //将环境光添加到场景中
}
// 屏幕自适应
canvasResize() {
window.addEventListener("resize", () => {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.CSS2Renderer.setSize(window.innerWidth, window.innerHeight);
});
}
// 模型加载
/**
* @param {Object} GLTFLoader 模型加载器
* @param {String} path 模型资源路径
*/
loadModel(GLTFLoader, path, isedit) {
return new Promise((resolve, reject) => {
this.gltfloader = new GLTFLoader();
this.gltfloader.load(
//下面是模型加载的路径
path,
(gltf) => {
// console.log(isedit);
gltf.scene.traverse((child) => {
this._forModels(child);
});
// 初始化场景位置
gltf.scene.position.set(-5, 0, 10);
this.scene.add(gltf.scene);
// 加载完后可执行函数
// console.log(isedit);
this._afterLoaded(gltf.scene, isedit);
resolve(gltf);
},
(xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
(error) => {
console.log(error);
reject(error);
}
);
});
}
// 模型加载完函数
_afterLoaded(scene, isedit) {
console.log(isedit);
// 可以进行选中
this._hoverModel(this.points, isedit);
// 可以进行点击
if (isedit == true) {
console.log('dada')
this._ClickModel(this.points);
}
//将墙壁进行隐藏
this.WallInit()
//对路牌进行相关操作
// this.SignsInf()
}
// 在此方法中对模型批量操作,这里遍历附着点
_forModels(child) {
// 匹配附着点(这里不适合对单个模型进行保存,批量对模型进行操作)
if (child.isMesh && /^point+/.test(child.name)) {
this.points.push(child);
// 改变为基础材质
child.material = new this.THREE.MeshBasicMaterial();
child.scale.set(0.01, 0.01, 0.01);
if (/b[rl]$/.test(child.name)) child.translateZ(2);
// 遍历一个属性是否存在设备
child.hasDevice = false; //初始化
}
// child.material.emissive = child.material.color
// child.material.emissiveMap = child.material.map
}
// 添加轨道控制器
/**
* @param {Object} OrbitControls 轨道控制器
*/
addOrbitControls(OrbitControls, isedit) {
this.orbitControls = new OrbitControls(
this.camera,
this.renderer.domElement
);
//初始化轨道控制器
this.orbitControls.object.position.set(-29, 18, -50);
// this.orbitControls.maxPolarAngle = (4 * Math.PI) / 7;
//下面的设置是使隧道的上下不进行旋转,只左右旋转即可
if (isedit == true) {
this.orbitControls.maxPolarAngle = (3.69 * Math.PI) / 7;
} else {
this.orbitControls.maxPolarAngle = (4 * Math.PI) / 8.5;
this.orbitControls.minPolarAngle = (4 * Math.PI) / 8.5;
}
this.orbitControls.minDistance = 5;
this.orbitControls.maxDistance = 87;
// 监听控制器变化
this.orbitControls.addEventListener("end", handleEndChange.bind(this));
this.orbitControls.addEventListener("start", handleStartChange.bind(this));
}
/**
* @param {Class} Tween 添加补间动画
*/
addTween(Tween) {
this.TWEEN = Tween;
}
/***
* 鼠标移入可以高亮的模型
*@param {Array} hoverModels 需要高亮的模型
*/
_hoverModel(hoverModels, isedit) {
this.renderer.domElement.addEventListener("mousemove", (e) => {
const mouse = new this.THREE.Vector2();
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
const raycaster = new this.THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
const intersects = raycaster.intersectObjects(hoverModels);
if (intersects.length > 0) {
if (!this.preHover) {
this.preHover = intersects[0].object;
} else {
if (isedit != true) {
console.log(isedit);
this._handleLHover(intersects[0].object);
}
this.preHover.material.color.set("white");
this.preHover = intersects[0].object;
}
intersects[0].object.material.color.set("red");
const tagP = intersects[0].object.getWorldPosition(
new this.THREE.Vector3()
);
this._checkAnimation(tagP);
} else {
if (!this.preHover) return;
this.preHover.material.color.set("white");
}
// this._handleLHover(intersects[0].object);
// this._handleLClick(intersects[0].object);
});
}
/**
* @param {Array} isClickModels 可以进行点击的模型吧
*/
_ClickModel(isClickModels) {
// 点击两次处理事件
this.renderer.domElement.addEventListener(
"dblclick",
this.__handleListenerDBLClickEvent.bind(this, isClickModels) //注意这里重新指向this第一个是修改this指向第二个是传入函数的参数
);
// 点击一次时处理时间
this.renderer.domElement.addEventListener(
"mousedown",
this.__handleListenerClickRLEvent.bind(this, isClickModels) //注意这里重新指向this第一个是修改this指向第二个是传入函数的参数
);
}
// 处理点击2次触发事件事件
__handleListenerDBLClickEvent(isClickModels, e) {
e.preventDefault();
// 如果监听到双击则清空单次点击事件
clearTimeout(this.EventTimer);
//最后一个是监听器默认参数
const mouse = new this.THREE.Vector2();
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
const raycaster = new this.THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
const intersects = raycaster.intersectObjects(isClickModels);
// 取消定位的视角返回初始化
if (intersects.length === 0) {
this.clearTagsObj();
this._resetState();
return;
}
// 处理连续双击击事件
this._handleDBLClick(intersects[0].object);
}
// 处理点击左右键触发的事件
__handleListenerClickRLEvent(isClickModels, e) {
e.preventDefault();
clearTimeout(this.EventTimer);
this.EventTimer = setTimeout(() => {
const mouse = new this.THREE.Vector2();
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
const raycaster = new this.THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
const intersects = raycaster.intersectObjects(isClickModels);
if (intersects.length === 0) return;
//附着点设置方框
this.setBoxHelper(intersects[0].object);
// 处理点击左右键事件
if (e.button === 0) {
this._handleLClick(intersects[0].object);
} else if (e.button === 2) {
this._handleRClick(intersects[0].object);
}
}, 400);
}
// 添加css3Renderer
addCSS3Renderer(CSS2Renderer, CSS2DObject, [infoDom, editDom]) {
this.CSS2Renderer = new CSS2Renderer();
this.CSS2Renderer.setSize(window.innerWidth, window.innerHeight - 100);
this.CSS2Renderer.render(this.scene, this.camera);
this.mountedElement.appendChild(this.CSS2Renderer.domElement);
// 初始化html标签
this.tagHtml = infoDom;
this.tag2Html = infoDom;
this.tag3Html = editDom;
this.CSS2DObject = CSS2DObject;
// 创建html标签模型
this.tagCSS2DObj = new CSS2DObject(this.tagHtml);
this.tag2CSS2DObj = new CSS2DObject(this.tag2Html);
this.tag3CSS2DObj = new CSS2DObject(this.tag3Html);
this.tagCSS2DObj.element.style.display = "none";
this.tag2CSS2DObj.element.style.display = "none";
this.tag3CSS2DObj.element.style.display = "none";
// // 设置该标签初始化透明
// this.tagCSS2DObj.element.style.opacity = "1";
// this.tag2CSS2DObj.element.style.opacity = "1";
// 下面是3D的下面把它注释之后方便来修正
this.tagCSS2DObj.scale.set(0.1, 0.1, 0.1);
this.tag3CSS2DObj.scale.set(0.02, 0.02, 0.02); //编辑框
this.tag3CSS2DObj.position.set(10, 0, 10);
// 下面是2D的显示方案
// this.tagCSS2DObj.scale.set(0.001, 0.001, 0.001);
// this.tag3CSS2DObj.scale.set(0.001, 0.001, 0.001); //编辑框
// this.tag3CSS2DObj.position.set(1, 0, 1);
}
clearTagsObj() {
if (this.preDBLModel) {
this.preDBLModel.remove(this.tagCSS2DObj);
}
// 所有标签看不见
this.scene.remove(this.tag2CSS2DObj);
this.scene.remove(this.tag3CSS2DObj);
this.tag3CSS2DObj.element.style.opacity = "0";
// 删除标记动画
this.scene.remove(this.group);
}
// 附着点选中线宽包围
setBoxHelper(obj) {
// 判断场景是否纯在该3d对象
if (this.scene.getObjectByName("boxHelper")) {
const preModel = this.scene.getObjectByName("boxHelper");
this.scene.remove(preModel);
}
const boxHelper = new this.THREE.BoxHelper(obj, 0xffff00);
boxHelper.name = "boxHelper";
this.scene.add(boxHelper);
}
/**
*@param {Class} DRACOLoader 解压器
* @param {String} dracoPath 解压器路径
*/
addDARCOLoder(DRACOLoader, dracoPath) {
this.DRAC = new DRACOLoader();
this.DRAC.setDecoderPath(dracoPath);
//给模型加载器添解压器
this.gltfloader.setDRACOLoader(this.DRAC);
}
/**
* @param {Map} meshes gltf加载过后的模型Map
*/
initDevicesModel(equMap) {
// 把风机叶片做为一个组,方便后期旋转
const group = new this.THREE.Group();
group.name = "fanLeaf";
// 初始化风机
this.equMap = equMap;
// 初始化风机颜色
this.equMap.get("equ_fan").traverse((v) => {
v.material = new this.THREE.MeshBasicMaterial();
// v.material.color = new this.THREE.Color(0xFF0000);
v.material.color = new this.THREE.Color(0xC0C0C0);
if (/^leaf/.test(v.name) || /^roller/.test(v.name)) {
group.add(v.clone());
v.visible = false;
// console.log(v);
}
});
// const axesHelper = new this.THREE.AxesHelper(100);
// 改变叶子旋转中心
let x = 0,
y = -0.2099,
z = 0;
const wrapper = new this.THREE.Object3D();
wrapper.position.set(x, y, z);
wrapper.add(group);
group.position.set(-x, -y, -z);
// wrapper.add(axesHelper);
wrapper.rotation.z = Math.PI / 4;
wrapper.name = "fan_leafs";
this.equMap.get("equ_fan").add(wrapper);
// 初其他传感器机颜色
this.equMap.get("equ_sensors").scale.set(0.2, 0.2, 0.2);
this.equMap.get("equ_sensors").traverse((v) => {
v.material = new this.THREE.MeshBasicMaterial();
v.material.color = new this.THREE.Color(0xababab);
});
// 初始标签面板
const tag = new this.THREE.Mesh(
new this.THREE.PlaneGeometry(7, 5),
new this.THREE.MeshBasicMaterial({ color: "white" })
);
this.equMap.get("equ_sensors").rotation.x += Math.PI / 2;
this.equMap.get("equ_sensors").add(tag);
tag.name = "tag";
tag.rotation.x = Math.PI / 2;
tag.rotation.z = Math.PI / 2; //旋转这里改变文字顺序
tag.rotation.y = Math.PI;
tag.rotation.z -= Math.PI / 2;
tag.translateZ(2.0);
}
/**
* @param {String} devId 删除的设备ID
*/
removeDevice(devId) {
removeDev.bind(this, devId);
}
/**
*
* @param {Number} distance 设置锚点之间的间隔距离
*/
setDistance(distance = 10) {
this.distance = distance;
}
loadBackground(hdrLoader, backColorSet) {
this.scene.background = new this.THREE.TextureLoader().load(
"/images/background/background.png",
function (texture) {
texture.encoding = backColorSet;
}
);
}
/**
*
* @param {Boolean} option 是否禁用轨道控制器
*/
isControlOrbit(option = true) {
this.orbitControls.enabled = option;
const mesh = this.scene.getObjectByName("chanel");
const mesh2 = this.scene.getObjectByName("chanel_1");
const mesh3 = this.scene.getObjectByName("chanel_2");
const mesh4 = this.scene.getObjectByName("chanel_3");
const opacityTween = (startVal, endVal, isNeedLookAt = false) => {
this.opacityTween = new this.TWEEN.Tween({
opacity: startVal,
lookX: this.orbitControls.target.x,
lookY: this.orbitControls.target.y,
lookZ: this.orbitControls.target.z,
});
this.opacityTween.to(
{
opacity: endVal,
lookX: 0,
lookY: 0,
lookZ: 0,
},
500
);
this.opacityTween.start();
this.opacityTween.onUpdate((obj) => {
// mesh.material.opacity = obj.opacity;
// mesh2.material.opacity = obj.opacity;
// mesh3.material.opacity = obj.opacity;
// mesh4.material.opacity = obj.opacity;
if (!isNeedLookAt) return;
this.camera.lookAt(obj.lookX, obj.lookY, obj.lookZ);
this.orbitControls.target.set(obj.lookX, obj.lookY, obj.lookZ);
});
};
if (!option) {
// 显示编辑框隧道颜色
opacityTween(0.5, 0.1);
} else {
// 退出编辑框什么颜色
opacityTween(0.1, 0.5, true);
}
}
//初始将墙壁进行隐藏
WallInit() {
for (let line = 1; line <= 20; line++) {
if (line < 10) {
let wall = 'wall_' + '0' + line
this.scene.getObjectByName(wall).visible = false
} else if (line >= 10) {
let wall = 'wall_' + line
this.scene.getObjectByName(wall).visible = false
}
}
}
SignsInf(tunnelName, tunnelLength) {
let Signs = this.scene.getObjectByName('streetSigns');
// console.log(Signs);
const tag = new this.THREE.Mesh(
new this.THREE.PlaneGeometry(100, 76),
new this.THREE.MeshBasicMaterial({ color: "white" })
);
Signs.add(tag);
tag.name = "tag";
tag.rotation.x = Math.PI / 2;
tag.rotation.z = Math.PI / 2; //旋转这里改变文字顺序
tag.rotation.y = Math.PI;
tag.rotation.z -= Math.PI / 2;
tag.translateZ(3);
tag.translateY(40);
let EquipmentTag = this._StreetSignTag(tunnelName, tunnelLength);
Signs.getObjectByName("tag").material = EquipmentTag;
}
}