<template>
  <!-- 三维画布 -->
  <div style="width:100%;height:1005px;position:absolute;top: 0px;left: 0">
    <div id="three_div" ref="draw" class="draw" />
    <div v-if="loadSuccess" class="loadingBox">
      <div class="progress">
        <div class="progress-bar progress-bar-danger progress-bar-striped active" :style="{ width: progress }">
          <div class="progress-value">{{ progress }}</div>
        </div>
      </div>
      <div class="loadingBox1">变电站三维模型加载中... 请稍候！</div>
    </div>
  </div>
</template>
<script setup>

import { onMounted, ref, reactive, onBeforeUnmount, watch } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' // 控制器
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js' // 控制器
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js' // 控制器
const props =defineProps({
  isautoRotate: {
    type: Boolean,
    default: false
  }
});
watch(() => props.isautoRotate, (newValue, oldValue) => {
  controls.autoRotate = newValue
}, { deep: true })


let renderer = reactive(null) // 渲染器
const draw = ref() // 场景div
let scene = ref(null) // 场景
const progress = ref(0) // 场景 - 加载进度条
const loadSuccess = ref(true) // 场景 - 加载进度条 显示
const clientWidth = ref()
const clientHeight = ref()
let defaultCamera = reactive(null) // 相机
let activeCamera = reactive(null)
let controls = reactive(null) // 相机控制器
let state = {
  wireframe: false, // 显示线框
  grid: false, // 显示坐标轴

  addLights: true,
  exposure: 2.0,
  textureEncoding: 'sRGB',
  ambientIntensity: 1,
  ambientColor: 0xFFFFFF,
  directIntensity: 0.8 * Math.PI,
  directColor: 0xcccccc,
  bgColor1: '#008157',
  bgColor2: '#040F11'
}

let _animate = null // 动画
let lights = []
let content = null


// 初始化渲染器
function initRenderer() {
  // 初始化场景
  clientWidth.value = draw.value.clientWidth
  clientHeight.value = draw.value.clientHeight
  // 初始化场景
  scene = new THREE.Scene()
  // 初始化相机
  defaultCamera = new THREE.PerspectiveCamera(60, clientWidth.value / clientHeight.value, 0.01, 1000)
  activeCamera = defaultCamera
  scene.add(defaultCamera)
  // 实例化渲染器
  renderer = window.renderer = new THREE.WebGLRenderer({ antialias: true })
  // renderer.physicallyCorrectLights = true
  renderer.useLegacyLights = true
  renderer.gammaOutput = true
  renderer.gammaFactor = 2.2
  renderer.setClearColor(0x07241c)
  // 设置设备像素比
  renderer.setPixelRatio(window.devicePixelRatio)
  // 设置渲染区域尺寸
  renderer.setSize(draw.value.clientWidth, draw.value.clientHeight)
  // 初始化相机控制器
  controls = new OrbitControls(defaultCamera, renderer.domElement)
  controls.autoRotate = true // 是否自动旋转
  controls.autoRotateSpeed = 1 // 旋转速度
  controls.screenSpacePanning = true
  draw.value.appendChild(renderer.domElement)
  // 运行动画
  animate()
  // 添加事件
  addmeth()
  // 加载场景模型
  initgltfLoader()
}
// 运行动画
function animate() {
  _animate = requestAnimationFrame(animate) // 循环调用函数
  controls.update()
  renderer.render(scene, activeCamera)
}
// 添加事件
function addmeth() {
  window.addEventListener('resize', resize, false)
  window.addEventListener('wheel', handleMouseWheel, false)
}
// 加载场景模型 gltf
function initgltfLoader() {
  const manager = new THREE.LoadingManager()
  manager.setURLModifier((url, path) => {
    return (path || '') + url
  })
  const gltfLoader = new GLTFLoader(manager)
  const dracoLoader = new DRACOLoader()
  dracoLoader.setDecoderPath('./static/js/')
  dracoLoader.setDecoderConfig({ type: 'js' })
  gltfLoader.setDRACOLoader(dracoLoader)
  gltfLoader.load('./static/3DModel/scene.glb', (gltf) => {
    const model = gltf.scene || gltf.scenes[0]
    const clips = gltf.animations || []
    setContent(model, clips)
    loadSuccess.value = false
  }, (xhr) => {
    const percentage = Math.floor(xhr.loaded / xhr.total * 100)
    progress.value = percentage + '%'
  })
}
// 模型属性设置
function setContent(object, clips) {
  clear()
  object.updateMatrixWorld()
  const box = new THREE.Box3().setFromObject(object)
  const size = box.getSize(new THREE.Vector3()).length()
  const center = box.getCenter(new THREE.Vector3())
  // 相机控制器重置
  controls.reset()

  object.position.x += (object.position.x - center.x)
  object.position.y = 17
  object.position.z += (object.position.z - center.z)
  controls.maxDistance = size * 10
  defaultCamera.near = size / 100
  defaultCamera.far = size * 100

  // 更新相机
  defaultCamera.updateProjectionMatrix()
  // 相机视角设置
  defaultCamera.position.copy(center)
  defaultCamera.position.x += size / 2
  defaultCamera.position.y += size / 2.8
  defaultCamera.position.z += size / 2.85
  defaultCamera.lookAt(center)

  content = object
  controls.saveState()
  // 添加模型
  scene.add(object)

  state.addLights = true;
  content.traverse((node) => {
    if (node.isLight) {
      state.addLights = false
    }
  })
  // 添加光照
  updateLights()

}
// 添加光照
function updateLights() {
  if (state.addLights && !lights.length) {
    addLights();
  } else if (!state.addLights && lights.length) {
    removeLights();
  }
  if(renderer == null){
    return
  }
  renderer.toneMappingExposure = state.exposure;
  if (lights.length === 2) {
    lights[0].intensity = state.ambientIntensity;
    lights[0].color.setHex(state.ambientColor);
    lights[1].intensity = state.directIntensity;
    lights[1].color.setHex(state.directColor);
  }
}
// 添加光照
function addLights() {
  const light1 = new THREE.AmbientLight(state.ambientColor, state.ambientIntensity)
  light1.name = 'ambient_light'
  defaultCamera.add(light1)

  const light2 = new THREE.DirectionalLight(state.directColor, state.directIntensity)
  light2.position.set(0.5, 0, 0.866)
  light2.name = 'main_light'
  defaultCamera.add(light2)

  lights.push(light1, light2)
}
// 删除光照
function removeLights() {
  lights.forEach((light) => light.parent.remove(light));
  lights.length = 0;
}
function clear() {
  if (!content) return
  scene.remove(content)
  content.traverse((node) => {
    if (!node.isMesh) return;
    node.geometry.dispose()

  })
  traverseMaterials(content, (material) => {
    MAP_NAMES.forEach((map) => {
      if (material[map]) material[map].dispose()
    })
  })
}
function removeObj(obj) {
  let arr = obj.children.filter((x) => x)
  arr.forEach((item) => {
    if (item.children.length) {
      removeObj(item)
    } else {
      item.clear()
    }
  })
  obj.clear()
  arr = null
}
function traverseMaterials(object, callback) {
  object.traverse((node) => {
    if (!node.isMesh) return;
    const materials = Array.isArray(node.material)
      ? node.material
      : [node.material];
    materials.forEach(callback);
  });
}
// 监听尺寸变化
function resize() {
  // 重置渲染器输出画布canvas尺寸
  clientWidth.value = draw.value.clientWidth
  clientHeight.value = window.innerHeight - 75
  const k = draw.value.clientWidth / (window.innerHeight - 75) // 窗口宽高比
  defaultCamera.aspect = k
  defaultCamera.updateProjectionMatrix()
  renderer.setSize(draw.value.clientWidth, window.innerHeight - 75)
}
function handleMouseWheel(event) {
  if (event.target.localName == 'canvas') {
    // 设置相机缩放比数值越大缩放越明显
    const factor = 2
    // 从鼠标位置转化为webgl屏幕坐标位置
    const glScreenX = (event.clientX / controls.domElement.width) * 2 - 1
    const glScreenY = -(event.clientY / controls.domElement.height) * 2 + 1
    const vector = new THREE.Vector3(glScreenX, glScreenY, 0)
    // 从屏幕向量转为3d空间向量
    vector.unproject(controls.object)
    // 相机偏移量
    vector.sub(controls.object.position).setLength(factor)
    if (event.deltaY < 0) {
      controls.object.position.add(vector)
      controls.target.add(vector)
    } else {
      controls.object.position.sub(vector)
      controls.target.sub(vector)
    }
    controls.update()
  }
}

onMounted(() => {
  initRenderer()
  //取消选中并且取消右键操作
  document.body.onselectstart = document.body.oncontextmenu = function () {
    return false
  }
});
// 页面退出
onBeforeUnmount(() => {
  window.removeEventListener('resize', resize)
  window.removeEventListener('wheel', handleMouseWheel)
  //取消选中并且取消右键操作
  document.body.onselectstart = document.body.oncontextmenu = null
  removeObj(scene)
  renderer && renderer.dispose()
  renderer.forceContextLoss()
  renderer.domElement = null
  renderer.content = null
  renderer = null
  cancelAnimationFrame(_animate)
  THREE.Cache.clear()
})


</script>
 
<style lang="scss" scoped>
#three_div {
  width: 100%;
  height: 100%;
}

//进度条
.loadingBox {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: #001b4a;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 98;
}
.loadingBox1 {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 98;
  color: #ffffff;
  font-size: 20px;
  margin-top: 60px;
}

.progress {
  height: 17px;
  background: #07241c;
  padding: 3px;
  overflow: visible;
  border-radius: 20px;
  border-top: 1px solid #000;
  border-bottom: 1px solid #7992a8;
  width: 300px;

  .progress-bar {
    border-radius: 20px;
    position: relative;
    animation: animate-positive 2s;
    float: left;
    width: 0;
    height: 100%;
    font-size: 12px;
    line-height: 20px;
    color: #fff;
    text-align: center;
    background-color: #209f84;
    -webkit-transition: width .6s ease;
    -o-transition: width .6s ease;
    transition: width .6s ease;

  }

  .active {
    animation: reverse stripes 0.40s linear infinite, animate-positive 2s;
  }

  .progress-value {
    display: none;
    padding: 3px 7px;
    font-size: 13px;
    color: #fff;
    border-radius: 4px;
    background: #191919;
    border: 1px solid #000;
    position: absolute;
    top: -40px;
    right: -10px;
  }

  .progress-value:after {
    content: "";
    border-top: 10px solid #191919;
    border-left: 10px solid transparent;
    border-right: 10px solid transparent;
    position: absolute;
    bottom: -6px;
    left: 26%;
  }
}

.progress-bar-striped {
  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
  background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
  -webkit-background-size: 40px 40px;
  background-size: 40px 40px;
}

@-webkit-keyframes stripes {
  from {
    background-position: 40px 0
  }

  to {
    background-position: 0 0
  }
}

@-o-keyframes stripes {
  from {
    background-position: 40px 0
  }

  to {
    background-position: 0 0
  }
}

@keyframes stripes {
  from {
    background-position: 40px 0
  }

  to {
    background-position: 0 0
  }
}

@-webkit-keyframes animate-positive {
  0% {
    width: 0;
  }
}

@keyframes animate-positive {
  0% {
    width: 0;
  }
}

</style>
