<template>
    <div>
        <canvas id="three"></canvas> 
        <div id="dom"></div>
        <div class="btnGroup">
            <div class="button" @click="toHomeView">
                主视角
            </div>
            <div class="button" @click="setLabel">
                {{ isAddLabel ? '添加标签' : '移除标签' }}
            </div>
            <div class="button" @click="walk">
                漫游
            </div>
        </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 { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader.js";
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'
import bimStore from '@/store/modules/bim';
import gsap from "gsap";

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([]);
let curve = ref(null);

const props = defineProps({
    background: { // 背景颜色
        default: '#fff',
        type: String
    },
    sceneUrl: { // 模型路径 
        default: '/jz/glb/scene.gltf',
        type: String
    },
    light: {
        default: '0xffffff',
        type: String
    }
})
const data = reactive({
    gltfObj: null,
    btnLabelName: '添加标签',
    isAddLabel: true
});
const { gltfObj, isAddLabel } = toRefs(data);

watch(() => props.sceneUrl, val => {
    init();
    loadSence();
});
let objArr = [];
let objM = [];
// 建筑树点击
Bus.on('clickBuild', (isParent) => {
    // Todo
    console.log('clickBuild', isParent);
    if (!isParent) {// 点击子级
        var clickName = bimStore().activateTree.clickName;
        // cleanColor();
        var Floor = gltfObj.value.scene.getObjectByName(clickName);
        // 染色
        // Floor.traverse(e => {
        //     e.material = new THREE.MeshLambertMaterial({
        //         color: 0x00ff00,
        //     });
        // })
        // 高亮轮廓
        outlineObj([Floor]);
        // 拉近场景
        nearCamera(Floor);
        return;
    }
    // 父级
    // toHomeView();
})
// 设备树点击
Bus.on('clickDevice', (isParent) => {
    if (!isParent) {// 点击子级
        var clickName = bimStore().activateDevice.clickName;
        var Floor = gltfObj.value.scene.getObjectByName(clickName);
        console.log(1, Floor);
        // 高亮轮廓
        outlineObj([Floor])
        // 拉近距离
        nearCamera(Floor);
        return;
    }
    // 父级
    // toHomeView();
})

// 系统树点击
Bus.on('clickApplication', e => {
    // Todo
    console.log('clickApplication');
})

onMounted(() => {
    init();
    loadSence();
    // 启动动画
    renderScene();
    // document.addEventListener("click", onMouseDown);
    document.addEventListener("mousemove", 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

    scene = new THREE.Scene();
    scene.background = new THREE.Color(props.background);
    const canvas = document.querySelector("#three");
    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(-92.936, 180.990, -28.179);
    camera.lookAt(scene.position);

    // 创建控件对象 限制controls的上下角度范围
    orbit = new OrbitControls(camera, renderer.domElement);
    orbit.maxPolarAngle = Math.PI / 2.1;
    orbit.maxDistance = 12;

    renderer.setSize(window.innerWidth, window.innerHeight);
    // 环境光 光源颜色从0x444444更改为0x888888,可以看到threejs场景中的网格模型表面变的更亮。
    var alight = new THREE.AmbientLight(props.light, 1);
    alight.name = "aLight";
    scene.add(alight);

    // 增加漫游路线
    makeCurve();
    
};
// 加载场景
const loadSence = () => {
    const gltfLoader = new GLTFLoader();
    gltfLoader.load(props.sceneUrl, (gltf) => {
        gltfObj.value = gltf;
        var model = gltf.scene;
        scene.add(model);
    });
};
// 渲染
const renderScene = () => {
    // orbit.update(); // 拖动
    // 使用requestAnimationFrame函数进行渲染
    requestAnimationFrame(renderScene);
    renderer.render(scene, camera);
    // 写在requestAnimationFrame之后,否则会报错
    labelRenderer.render(scene, camera);
    if (composer) {
        composer.render()
    }
};
// 相机移动动画
const makeCurve = () => {
  curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(11.5, 0, 18),
    new THREE.Vector3(11.5, 0, 34),
    new THREE.Vector3(35, 0, 34),
    new THREE.Vector3(35, 0, 31),
    new THREE.Vector3(11.5, 0, 31),
  ]);
  // 为曲线添加材质在场景中显示出来，不显示也不会影响运动轨迹，相当于一个Helper
  const points = curve.getPoints(0.1);
  const geometry = new THREE.BufferGeometry().setFromPoints(points);
  const material = new THREE.LineBasicMaterial({
    color: 0xff0000,
  });

  // 线条模型对象
  const line = new THREE.Line(geometry, material);
  scene.add(line)
  
}

const changePosition = (t) => {
  const position = curve.getPointAt(t); // t: 当前点在线条上的位置百分比，后面计算
  mesh.position.copy(position);
}

const changeLookAt = (t) => {
  const tangent = curve.getTangentAt(t);
  const lookAtVec = tangent.add(mesh.position); // 位置向量和切线向量相加即为所需朝向的点向量
  mesh.lookAt(lookAtVec);
}

const loopTime = 10 * 1000; // loopTime: 循环一圈的时间
const walk = () => {
    let time = Date.now();
    setInterval(() => {
        let t = (time % loopTime) / loopTime; // 计算当前时间进度百分比
        changePosition(t);
        changeLookAt(t);
    }, 1000)
}

// 储存被选中的模型和材质
let selectedObject = null;
let selectedMaterial = null;
// 能选中的组
const enableGroup = [
    'guan2',
    'set2',
    'set3',
    'set4',
    'yuanliao_room',
    'tanghua_room',
    'touliao_room',
]
const onMouseMove = (event) => {
    var raycaster = new THREE.Raycaster()
    // 计算鼠标点击位置的归一化设备坐标
    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;
        if (enableGroup.indexOf(selectedObject.name) != -1) {
            outlineObj([selectedObject])
            // nearCamera(intersects);
        }
        if (enableGroup.indexOf(selectedObject.parent.name) != -1) {
            outlineObj([selectedObject.parent])
            // nearCamera(intersects);
        }
    }
}
const onMouseDown = (event) => {
    var raycaster = new THREE.Raycaster()
    // 计算鼠标点击位置的归一化设备坐标
    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;
        if (enableGroup.indexOf(selectedObject.name) != -1) {
            outlineObj([selectedObject])
            alert("点击了",selectedObject.name)
            // nearCamera(intersects);
        }
        if (enableGroup.indexOf(selectedObject.parent.name) != -1) {
            outlineObj([selectedObject.parent])
            alert("点击了",selectedObject.parent.name)
            // nearCamera(intersects);
        }
    }
}

let composer = null
let outlinePass = null
let renderPass = null
const outlineObj = (selectedObject_list) => {
    // 创建一个EffectComposer（效果组合器）对象，然后在该对象上添加后期处理通道。
    composer = new EffectComposer(renderer)
    // 新建一个场景通道  为了覆盖到原理来的场景上
    renderPass = new RenderPass(scene, camera)
    composer.addPass(renderPass);
    // 物体边缘发光通道
    outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera, selectedObject_list)
    outlinePass.selectedObjects = selectedObject_list
    // 下面都注掉。不然太开了
    outlinePass.edgeStrength = 10.0 // 边框的亮度
    outlinePass.edgeThickness = 1.0 // 边框宽度
    outlinePass.visibleEdgeColor.set(0x00ff00) // 边框颜色
    composer.addPass(outlinePass)
    // 自定义的着色器通道 作为参数
    var effectFXAA = new ShaderPass(FXAAShader)
    effectFXAA.uniforms.resolution.value.set(1 / window.innerWidth, 1 / window.innerHeight)
    effectFXAA.renderToScreen = true
    composer.addPass(effectFXAA)
}

const nearCamera = (floor) => {
    orbit.reset();
    gsap.to(camera.position, {
        x: floor.position.x - 40,
        y: floor.position.y + 60,
        z: floor.position.z,
        duration: 2,
        ease: "power1.inOut",
        onComplete: () => {
        },
    });
    
}
const toHomeView = () => {
    orbit.reset();
    outlineObj([]);
}


const setLabel = () => {
    if(isAddLabel.value) {
        addLabel();
    } else {
        removeLabel();
    }
    isAddLabel.value = !isAddLabel.value;
}

const removeLabel = () => {
    document.body.removeChild(labelRenderer.domElement);
}
// 添加标签
const addLabel = () => {
    let obj = gltfObj.value.scene.getObjectByName('set2');
    let text = "设备二";
    let 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 cleanColor = () => {
    // 恢复之前被选中模型的材质
    if (objArr.length > 0 && objM.length > 0) {
        objArr.forEach((ele, index) => {
            console.log(objM[index]);
            ele.material = objM[index]
        })
        objArr = [];
        objM = [];
    }
    gltfObj.value.scene.traverse(e => {
        objArr.push(e);
        objM.push(e.material);
    })
}
</script>
<style lang='scss'>
#three {
    height: 100%;
    width: 100%;
}

.btnGroup {
    width: 180px;
    position: absolute;
    left: 0px;
    bottom: 40px;
    z-index: 999;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    cursor: pointer;
    color: #ffff;

    >div {
        background: #477efa;
        width: 120px;
        height: 40px;
        border-radius: 0 50px 50px 0;
        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-top-right-radius: 12px;
    border-bottom-right-radius: 12px;
    font-size: 12px;
    display: flex;
    flex-direction: column;

    &::before {
        // content: "";
        // display: inline-block;
        // width: 0;
        // height: 0;
        // border: 12px solid transparent;
        // border-right-color: #ffffffb3;
        // position: absolute;
        // left: -23px;
        // top: 0;
    }

    .arrow_outer {
        // position: absolute;
        // left: -22px;
        // top: 6px;
        // margin: 0;
        // width: 12px;
        // height: 12px;
        // background-color: #eb6852;
        // border-radius: 100%;
        // border: 1px solid #fff;
    }

    span {
        line-height: 24px;
    }
}
</style>