three.js 记录

var THREE = require(‘three‘);

import { OrbitControls } from ‘three/examples/jsm/controls/OrbitControls.js‘;
import { FBXLoader } from ‘three/examples/jsm/loaders/FBXLoader.js‘;
import Stats from ‘three/examples/jsm/libs/stats.module.js‘;
import Zlib from ‘three/examples/jsm/libs/inflate.module.min.js‘;

import { CSS2DRenderer, CSS2DObject } from ‘three/examples/jsm/renderers/CSS2DRenderer.js‘;
import {
fjFacti, cdzsqFacti, sxtFacti, lightFacti, qbbfFacti, qbbmFacti, coviFacti, xfsbFacti, hztcqFacti, jjdhFacti, fsfxFacti, kzsFacti, kbxsFacti, cljcqFacti, jlmFacti,
hldFacti, dngqFacti, bxFacti, cdzsqFactiNo
} from ‘./loadFactites.js‘
import { getFacs, getHoles, listPartitionByHoleId } from "@/api/mainsys.js"
import { showPop } from ‘./pop‘;

var container, stats, controls;
var camera, scene, renderer, light, labelRenderer;
var vueObj;

// 创建一个时钟对象Clock
//var clock = new THREE.Clock();

var groupSb = new THREE.Group();//设备模型
var fjArray = [];//风机模型数组
var fjdirection;//风机旋转方向控制变量

//var textLoader = new THREE.FontLoader();//文字加载器

var speedRight = null;//可变限速数值文字模型(右洞)
var kbxsRightSb = null;//可变限速模型(右洞)

var speedLeft = null;//可变限速数值文字模型(左洞)
var kbxsLeftSb = null;//可变限速模型(左洞)

var textM = null;//可变信息板门架式文字模型
var qbbmSb = null;//可变信息板门架式模型
var mapM = 0;//循环控制文字显示
var textMwidth;//纹理X坐标

var textF = null;//可变信息板F式文字模型
var qbbfSb = null;//可变信息板F式模型
var mapF = 0;//循环控制文字显示
var intervalF = null;

var ani = null;
var ani2 = null;

const toResize = () => {
renderer.setSize(container.offsetWidth, container.offsetHeight);
}//窗口自适应函数

var loader = new FBXLoader();

export function init(obj) {

vueObj = null;
vueObj = obj;
container = document.getElementById(‘info‘);
console.log(‘vueObj‘, vueObj)

camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2525000);

// camera.rotation.set( 30,0,0)
// 摄像机位置
camera.position.set(60000, 7000, 37000);
// camera.zoom = -3
controls = new OrbitControls(camera, container);
// 控制缩放范围
controls.maxDistance = 252500;
controls.minDistance = 0;
// 控制垂直旋转角度
controls.maxPolarAngle = 1.5
controls.minPolarAngle = 0.1

// controls.enablePan = false
// 旋转中心点
//controls.target.set(1000, 0, 1000);
controls.update();
//场景
scene = new THREE.Scene();
// scene.background = new THREE.Color( ‘#62A5F4‘ );
// scene.fog = new THREE.Fog( 0xa0a0a0, 200, 1000 );

light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
light.position.set(0, 2000, 0);
scene.add(light);

// 坐标轴工具类
var axesHelper = new THREE.AxesHelper(35000);
scene.add(axesHelper);

light = new THREE.DirectionalLight(0xffffff, 0.7);
light.position.set(0, 200, 100);
light.castShadow = true;
light.shadow.camera.top = 180;
light.shadow.camera.bottom = -100;
light.shadow.camera.left = -120;
light.shadow.camera.right = 120;
scene.add(light);
// 模拟相机视锥体的辅助对象
// scene.add( new THREE.CameraHelper( light.shadow.camera ) );

// var grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
// grid.material.opacity = 0.2;
// grid.material.transparent = true;
// scene.add( grid );
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setClearAlpha(0.2);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(container.offsetWidth, container.offsetHeight);
renderer.shadowMap.enabled = true;
document.getElementById("info").appendChild(renderer.domElement);

window.addEventListener(‘resize‘, toResize)

// model
// var loader = new FBXLoader();
// 地面模型
loader.load(‘../../static/threed/隧道三车道.FBX‘, function (object) {
console.log(‘loader‘, loader)
object.name = "dm";
scene.add(object);

// console.log(object)

// object.children.forEach((item,index)=>{
//   // if(index===7) item.translateY(10000)
//   // if(index===8) item.translateY(10000)
//   // if(index===9) item.translateY(10000)
//   //if(index===15) item.translateY(10000)
//   //if(index===15) item.translateY(10000)
// })

});

// function getTextCanvas(text) {
// var width = 512, height = 256;
// var canvas = document.createElement(‘canvas‘);
// canvas.width = width;
// canvas.height = height;
// var ctx = canvas.getContext(‘2d‘);
// ctx.fillStyle = ‘#C3C3C3‘;
// ctx.fillRect(0, 0, width, height);
// ctx.font = 50 + ‘px " bold‘;
// ctx.fillStyle = ‘#2891FF‘;
// ctx.textAlign = ‘center‘;
// ctx.textBaseline = ‘middle‘;
// ctx.fillText(text, width / 2, height / 2);
// return canvas;
// }
// var geometry = new THREE.PlaneBufferGeometry(512, 256);
// var material = new THREE.MeshBasicMaterial({ map: new THREE.CanvasTexture(getTextCanvas(‘我是你爸爸‘)) });// top
// var mesh = new THREE.Mesh(geometry, material);
// scene.add(mesh)
// mesh.scale.set(10, 10, 10)

// 基础设备 包含控制室 车道白线等
getBaseSB().then(obj => {
getBaseFacl(obj)

})

// window.addEventListener( ‘resize‘, onWindowResize, false );

// window.addEventListener( ‘mousemove‘, dragScence, false );

// 隧道所用设备 风机 情报板等等
getSB().then(obj => {
getFacl(obj)
});

container.addEventListener(‘mousedown‘, onDocumentMouseDown, false);
animate()

}

function animate() {
ani = requestAnimationFrame(animate);
fjRotate();
fontRun();
controls.update();
renderer.render(scene, camera);
}
async function getBaseSB() {
let kzs = await kzsFacti();
let bx = await bxFacti();
let o = { kzs, bx };
return o;
}
async function getSB() {

let fj = await fjFacti();
let cdzsq = await cdzsqFacti();
let sxt = await sxtFacti();
let light = await lightFacti();
let qbbf = await qbbfFacti();
let qbbm = await qbbmFacti();
let covi = await coviFacti();
let xfsb = await xfsbFacti();
let hztcq = await hztcqFacti();
let jjdh = await jjdhFacti();
let fsfx = await fsfxFacti();
let kbxs = await kbxsFacti();
let hld = await hldFacti();
let dngq = await dngqFacti();
let cljcq = await cljcqFacti();
let jlm = await jlmFacti();
let o = { fj, cdzsq, sxt, light, qbbf, qbbm, covi, xfsb, hztcq, jjdh, fsfx, hld, dngq, kbxs, cljcq, jlm };
return o;
}

function onDocumentMouseDown(event) {

event.preventDefault();
var mainCanvas = event.path[0];

if (scene) {

// 清空当前弹框

clearLabel();
var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2();


mouse.x = ((event.clientX - mainCanvas.getBoundingClientRect().left) / mainCanvas.offsetWidth) * 2 - 1;
mouse.y = - ((event.clientY - mainCanvas.getBoundingClientRect().top) / mainCanvas.offsetHeight) * 2 + 1;



raycaster.setFromCamera(mouse, camera); // 计算物体和射线的焦点
//console.log("mouse--",mouse)
var intersects = raycaster.intersectObjects(scene.children, true);
//console.log("点击后相交的物体位置", intersects[0].point.x, intersects[0].point.y, intersects[0].point.z)

// 递归找到点击的是属于该点击处的模型名称
var str = [];
if (intersects.length > 0) {
  console.log(‘3d坐标----------》》》》》》》》》》‘, intersects[0].point)
  for (var i = 0; i < intersects.length; i++) {

    // 递归找到点击的是属于该点击处的模型
    findMesh(intersects[i].object, str);

  }

  //console.log("模型",str)
  let num = true;
  for (var item of str) {
    let name = item.name
    if (num === false) return;
    //开启点击弹窗的模型
    let array = ["fj", "cdzsq", "sxt", "qbbm", "qbbf", "covi", "xfsb", "hztcq", "hld", "dngq", "fsfx", "jjdh", "sxt", "kbxs", "cljcq", "jlm"]
    if (array.indexOf(name) !== -1) {
      num = false;
      console.log("点击得到的数据》》》》》》》", item)
      showMsg(item, item.userData)

    }
  }
}

}

}

// 递归找到点击的模型下所有的材质
function findMesh(obj, str) {
if (obj.parent != null) {
findMesh(obj.parent, str);
return str.push(obj.parent)
}

}

// 加载弹框信息
/*
加载弹框信息
params obj 点击的3d对象,o是该设备信息
*/
function showMsg(obj, o) {

clearLabel();

var earthDiv = document.createElement(‘div‘);

//生成不同的弹框
showPop(earthDiv, o, function (newState) {
clearLabel();
if (!newState) return;
var oldState = o.defaultCommandStatus;
o.defaultCommandStatus = newState;
cdzsqChangeState(obj);
hldChangeState(obj);
jlmChangeState(obj)
}, vueObj)
// earthDiv.style.marginTop = ‘-1em‘;
var earthLabel = new CSS2DObject(earthDiv);
earthLabel.name = "label";
// earthLabel.position.set(0, obj.position.y + 10, 0);
obj.add(earthLabel);
labelRender();

}
function labelRender() {

if (labelRenderer) {

// new CSS2DRenderer().dispatchEvent("removed")

// 确保3D相关对象已销毁
cancelAnimationFrame(ani2) // 可以取消动画

if (document.getElementsByClassName("label").length > 0) {
  document.getElementsByClassName("label")[0].parentNode.removeChild(document.getElementsByClassName("label")[0])
  //console.log("document.getElementsByClassName--------",document.getElementsByClassName("label")[0])

}

}
labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(container.offsetWidth, container.offsetHeight);
labelRenderer.domElement.style.position = ‘absolute‘;
labelRenderer.domElement.style.top = ‘180px‘;
labelRenderer.domElement.className = "labelRenderer";
document.getElementById("info").appendChild(labelRenderer.domElement);

// var controls = new OrbitControls( camera ,labelRenderer.domElement);
// // 控制缩放范围
// controls.maxDistance= 252500;
// controls.minDistance = 0;
// // 控制垂直旋转角度
// controls.maxPolarAngle =1.5
// controls.minPolarAngle = 0.1

// controls.enablePan = false
// 旋转中心点
// controls.target.set( 1000, 0, 1000 );
// controls.update();
animate2();
}
function animate2() {
ani2 = requestAnimationFrame(animate2);
if (renderer) {
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
}
}

export function clearScene() {

// // 这是清空
// if (scene) {
// clearLabel();//清空对应的2d
// for (let i = 0; i < scene.children.length; i++) {
// // 清空设备层
// for (let item of scene.children) {
// scene.remove(item);
// }
// }
// for (let item of scene.children) {
// if (item.name == "sb") {
// scene.remove(item);
// }
// }

// }

if (container.childNodes[0]) {
container.removeChild(container.childNodes[0])
}
// scene.dispose();
// if (renderer) {
// renderer.dispose();
// }
// cancelAnimationFrame(ani)

function dispose(parent, child) {
if (child.children.length) {
let arr = child.children.filter(x => x);
arr.forEach(a => {
dispose(child, a)
})
}
if (child instanceof THREE.Mesh) {
// console.log(‘item‘, child)
if (Array.isArray(child.material) && child.material.length > 0) {
child.material.forEach(item => {
if (item.map) item.map.dispose();
item.dispose();
})
}
else {
if (child.material.map) child.material.map.dispose();
child.material.dispose();
}
child.geometry.dispose();
} else if (child.material) {
child.material.dispose();
}
child.remove();
parent.remove(child);
}

let arr = scene.children.filter(x => x)
arr.forEach(a => {
dispose(scene, a);
})

window.removeEventListener("resize", toResize);

scene.dispose();
if (renderer) {
console.log(‘查看memery字段即可‘, renderer.info) //查看memery字段即可
renderer.renderLists.dispose();
renderer.dispose();
renderer.forceContextLoss();
renderer.domElement = null;
renderer.content = null;
renderer = null;
}
cancelAnimationFrame(ani);
cancelAnimationFrame(ani)
THREE.Cache.clear();
}

function clearLabel() {

let ll = scene.getObjectByName("label");
if (ll) {
ll.parent.remove(ll)
}
if (document.getElementsByClassName("labelRenderer").length > 0) {
document.getElementsByClassName("labelRenderer")[0].parentNode.removeChild(document.getElementsByClassName("labelRenderer")[0])

}
let ll2 = scene.getObjectByName("labelRenderer");
//console.log("ll2.domElement------------",ll2)
if (ll2) {
//console.log("ll2.domElement------------222",ll2.domElement)
ll2.domElement.parent.remove(ll2.domElement)
ll2.parent.remove(ll2)
ll2.dispose();

}
}

export function addModel(obj) {
clearScene();
init(obj);
console.log(THREE.Cache)
}

function getBaseFacl(obj) {
var group = new THREE.Group();
group.name = "basesb";
let sb = {};
// 控制室模型
// let kzsPos = [{ x: 15408, y: -2236, z: 15000 }];
// obj.kzs.userData = { id: "id_kzs" }
// let kzs = obj.kzs.clone();
// kzs.rotateY(Math.PI);
// kzs.position.copy(new THREE.Vector3(kzsPos[0].x, kzsPos[0].y, kzsPos[0].z))
// group.add(kzs)
// let kzsPos1 = [{ x: 28080, y: -1500, z: -15757 }];
// obj.kzs.userData = { id: "id_kzs" }
// let kzs1 = obj.kzs.clone();
// kzs1.rotateY(Math.PI);
// kzs1.position.copy(new THREE.Vector3(kzsPos1[0].x, kzsPos1[0].y, kzsPos1[0].z))
// group.add(kzs1)
// let kzsPos2 = [{ x: -21202, y: -2085, z: -15757 }];
// obj.kzs.userData = { id: "id_kzs" }
// let kzs2 = obj.kzs.clone();
// // kzs2.rotateY(Math.PI);
// kzs2.position.copy(new THREE.Vector3(kzsPos2[0].x, kzsPos2[0].y, kzsPos2[0].z))
// group.add(kzs2)
let kzsPos3 = [{ x: -29000, y: 1250, z: 22000 }];
obj.kzs.userData = { id: "id_kzs" }
let kzs3 = obj.kzs.clone();
kzs3.position.copy(new THREE.Vector3(kzsPos3[0].x, kzsPos3[0].y, kzsPos3[0].z))
kzs3.scale.set(1.2, 1.2, 1.2);
group.add(kzs3)

scene.add(group);
}
function getFacl(obj) {

// var array = [‘fj‘, ‘cdzsq‘, ‘sxt‘, ‘qbbm‘, ‘qbbf‘, ‘covi‘, ‘hztcq‘, ‘kbxs‘, ‘jjdh‘, ‘light‘]//普瑞不需要加载的模型:xfsb,hld
// for (let key in obj) {
// obj[key].visible = array.indexOf(obj[key].name) === -1 ? false : true;
// }

let tunnerid = vueObj.$store.getters.tunnerid;
let arr = ["?tunnelId=" + tunnerid];

getFacs(arr.join(‘‘)).then(async response => {
let { code, value } = response.data;
console.log(‘全部设备信息‘, value)
// 单洞信息
await getHoles(tunnerid).then(response => {
let { code, value } = response.data;
if (value && value.length > 0) {

    value.forEach(item => {

      if (item.direction == ‘left‘) {

        vueObj.hole_1 = item;

      } else if (item.direction == ‘right‘) {

        vueObj.hole_2 = item;

      } else {
        return;
      }
    })
  }

});
// 取得分区数据
var sorted = groupBy(value, function (item) {
  return [item.holeId];
});
if (sorted.length > 0) {

  vueObj.hole_1_list = vueObj.hole_1.id == sorted[0][0].holeId ? sorted[0] : sorted[1];//左洞设备信息
  vueObj.hole_2_list = vueObj.hole_2.id == sorted[0][0].holeId ? sorted[0] : sorted[1];//右洞设备信息

} else {
  vueObj.hole_1_list = [];
  vueObj.hole_2_list = [];
}
var hole_1_type = groupBy(vueObj.hole_1_list, function (item) {
  return [item.classifyCode];
});
var hole_2_type = groupBy(vueObj.hole_2_list, function (item) {
  return [item.classifyCode];
});
// 转换为子数组
toSonArr(hole_1_type, 1)
toSonArr(hole_2_type, 2)

var group = groupSb;
group.name = "sb";
let sb = {};


// 左洞的比例
let left_bl = 86000 / vueObj.hole_1.finishPosition;
let right_bl = 86000 / vueObj.hole_2.finishPosition;



let lightPos = [{ x: -30000, y: 4400, z: -12800 }, { x: -30000, y: 4400, z: -4150 }, { x: -30000, y: 4400, z: 4150 }, { x: -30000, y: 4400, z: 12800 }];//灯
for (let i = 0; i < 12; i++) {
  // 加上设备信息
  obj.light.userData = { id: "id_light_right1" + i }
  let sb = obj.light.clone();
  // sb.rotateY(40);
  sb.rotateX(-Math.PI / 2);
  sb.position.copy(new THREE.Vector3(lightPos[0].x + 5200 * i, lightPos[0].y, lightPos[0].z))
  if (i >= 10) {
    sb.position.copy(new THREE.Vector3(lightPos[0].x + 5200 * i + 1500, lightPos[0].y, lightPos[0].z - 3700))
  }
  group.add(sb)
}
for (let i = 0; i < 12; i++) {
  // 加上设备信息
  obj.light.userData = { id: "id_light_right2" + i }
  let sb = obj.light.clone();
  sb.position.copy(new THREE.Vector3(lightPos[1].x + 5200 * i, lightPos[1].y, lightPos[1].z))
  if (i >= 10) {
    sb.position.copy(new THREE.Vector3(lightPos[1].x + 5200 * i + 1500, lightPos[1].y, lightPos[1].z))
  }
  group.add(sb)
}
for (let i = 0; i < 12; i++) {
  // 加上设备信息
  obj.light.userData = { id: "id_light_left1" + i }
  let sb = obj.light.clone();
  sb.rotateX(-Math.PI / 2);
  sb.position.copy(new THREE.Vector3(lightPos[2].x + 5200 * i, lightPos[2].y, lightPos[2].z))
  if (i >= 10) {
    sb.position.copy(new THREE.Vector3(lightPos[2].x + 5200 * i + 1500, lightPos[2].y, lightPos[2].z))
  }
  group.add(sb)
}
for (let i = 0; i < 12; i++) {
  // 加上设备信息
  obj.light.userData = { id: "id_light_left2" + i }
  let sb = obj.light.clone();
  sb.position.copy(new THREE.Vector3(lightPos[3].x + 5200 * i, lightPos[3].y, lightPos[3].z))
  if (i >= 10) {
    sb.position.copy(new THREE.Vector3(lightPos[3].x + 5200 * i + 1500, lightPos[3].y, lightPos[3].z + 3700))
  }
  group.add(sb)
}



let vd = vueObj.$data
let keysArr = Object.keys(vd)
//console.log(‘vueObj:-------‘,vueObj.$data)

keysArr.forEach(item => {
  // console.log(item,item.indexOf(‘_‘,item.length - 2))
  if (Array.isArray(vd[item]) && item.indexOf(‘_‘, item.length - 2) > 0) {
    //console.log(‘item,vd[item]‘,item,vd[item])
    localSb(vd[item], obj, left_bl, right_bl, group)

  }
})
scene.add(group);

})
}
// 设备定位
async function localSb(list, obj, left_bl, right_bl, group) {

let sdLeftStartPosition = -42600;
let sdRightStartPosition = -42600;
if (list.length > 0) {

for (let i = 0; i < list.length; i++) {
  console.log(list[i].name, list[i].typeCode, list[i])
  let { typeCode } = list[i]
  let param, posLeft, posRight, rotx = 0, roty = 0
  switch (typeCode) {
    case ‘TrafficLights‘:
      param = ‘hld‘;
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = right_bl * Math.abs(list[i].position) + sdRightStartPosition - 7000
        y = 3800
        z = 13400
        roty = Math.PI
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition + 7000
        y = 3800
        z = -16700
      }
      sb.children[0].children[4].children[0].visible = false;//绿灯
      sb.children[0].children[4].children[1].visible = true;//绿灯灰色
      sb.children[0].children[5].children[1].visible = false;//黄灯
      sb.children[0].children[5].children[2].visible = true;//黄灯灰色
      sb.children[0].children[6].children[1].visible = false;//红灯
      sb.children[0].children[6].children[2].visible = true;//红灯灰色
      sb.children[0].children[7].children[2].visible = false;//箭头
      sb.children[0].children[7].children[1].visible = true;//箭头灰色
      sb.children[0].children[7].children[3].visible = false;//清楚箭头边缘绿色
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      //sb.scale.set(1)
      group.add(sb);
      hldChangeState(sb);
      break;
    case ‘Camera‘:
      param = ‘sxt‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        rotx = Math.PI / 2
        roty = Math.PI
      } else {
        rotx = Math.PI / 2
      }
      x = right_bl * Math.abs(list[i].position) + sdLeftStartPosition;
      var direction = d.direction === ‘right‘ ? 1 : -1;
      y = 4000;
      z = d.laneNum === 4 ? direction * 16700 : direction * 13200;
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      sb.rotateX(rotx)
      group.add(sb)
      break;
    case ‘CoViDetector‘:
      param = ‘covi‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = left_bl * Math.abs(list[i].position) + sdRightStartPosition
        y = 3500
        z = 13580
      } else {
        x = right_bl * Math.abs(list[i].position) + sdLeftStartPosition
        y = 3500
        z = -13750
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.scale.set(10, 10, 10);
      group.add(sb)
      break;
    case ‘FireFightingPump‘:
      param = ‘xfsb‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = left_bl * Math.abs(list[i].position) + sdRightStartPosition
        y = -2000
        z = 14700
        //roty = Math.PI
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition
        y = -2000
        z = -14700
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      group.add(sb)
      break;
    case ‘FireDetector‘:
      param = ‘hztcq‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        y = 1500
        z = d.position >= 645 ? 17700 : 14400;
        roty = Math.PI
      } else {
        y = 1500
        z = d.position >= 645 ? -17700 : -14400;
      }
      x = d.position >= 645 ? left_bl * Math.abs(list[i].position) + sdLeftStartPosition + 1000 : left_bl * Math.abs(list[i].position) + sdLeftStartPosition;
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      group.add(sb)
      break;
    case ‘EmergencyCallBroadCast‘:
      param = ‘jjdh‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = left_bl * Math.abs(list[i].position) + sdRightStartPosition
        y = -600
        z = 10250
        roty = Math.PI
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition
        y = -600
        z = -10250
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      group.add(sb)
      break;
    case ‘WindSpeedDetector‘:
      param = ‘fsfx‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = left_bl * Math.abs(list[i].position) + sdRightStartPosition
        y = 3500
        z = 13600
        rotx = -Math.PI / 2
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition
        y = 3500
        z = -13600
        rotx = Math.PI / 2
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateX(rotx)
      group.add(sb)
      break;
    case ‘JetFans‘:
      param = ‘fj‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      x = right_bl * Math.abs(list[i].position) + sdLeftStartPosition;
      var direction = d.direction === ‘right‘ ? 1 : -1;
      switch (d.laneNum) {
        case 1:
          y = 5000;
          z = 5000 * direction;
          break;
        case 2:
          y = 5900;
          z = 8500 * direction;
          break;
        case 3:
          y = 5000;
          z = 12000 * direction;
          break;

      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      group.add(sb)
      fjArray.push(sb)
      break;
    case ‘LaneLights‘:
      param = ‘cdzsq‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      x = right_bl * Math.abs(list[i].position) + sdLeftStartPosition;
      y = 5300;
      var direction = d.direction === ‘right‘ ? 1 : -1;
      switch (d.laneNum) {
        case 1:
          z = 5000 * direction;
          break;
        case 2:
          z = 8500 * direction;
          break;
        case 3:
          z = 12000 * direction;
          break;
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      group.add(sb)
      cdzsqChangeState(sb)
      break;
    case ‘PRFlexMessageBroadFType‘:
      param = ‘qbbf‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = right_bl * Math.abs(list[i].position) + sdRightStartPosition - 2000
        y = 2400
        z = 13400
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition + 2000
        y = 2400
        z = -16500
        roty = Math.PI
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      sb.scale.set(1, 1.2, 1.2)
      group.add(sb)
      var textContent = d.displayContent ? d.displayContent : ‘暂无内容‘;
      qbbfSb = sb
      qbbfChangeState(textContent);
      break;
    case ‘PRFlexMessageBroad‘:
      param = ‘qbbm‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = right_bl * Math.abs(list[i].position) + sdRightStartPosition - 2000
        y = 2250
        z = 8400
        roty = Math.PI
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition + 2000
        y = 2250
        z = -5900
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      sb.scale.set(0.3, 0.8, 1.05);
      group.add(sb)
      var textContent = d.displayContent ? d.displayContent : ‘暂无内容‘;
      qbbmSb = sb
      qbbmChangeState(textContent);
      break;
    case ‘PRFlexSpeedBroad‘:
      param = ‘kbxs‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;

      if (d.direction == ‘right‘) {
        x = right_bl * Math.abs(list[i].position) + sdRightStartPosition - 10000
        y = 1700
        z = 2500
        roty = Math.PI
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition + 10000
        y = 1700
        z = -2500
      }
      sb.position.copy(new THREE.Vector3(x, y, z));
      sb.rotateY(roty)
      group.add(sb)
      var number = d.displaySpeed ? d.displaySpeed : 0;
      if (d.direction == ‘right‘) {
        kbxsRightSb = sb
      }
      else {
        kbxsLeftSb = sb
      }
      kbxsChangeState(number, d.direction);
      break;

    case ‘FlexMessageBroadFType‘:
      param = ‘qbbf‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = right_bl * Math.abs(list[i].position) + sdRightStartPosition - 2000
        y = 2400
        z = 13400
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition + 2000
        y = 2400
        z = -16500
        roty = Math.PI
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      sb.scale.set(1, 1.2, 1.2)
      group.add(sb)
      var textContent = d.displayContent ? d.displayContent : ‘暂无内容‘;
      qbbfSb = sb
      qbbfChangeState(textContent);
      break;
    case ‘FlexMessageBroadPortalType‘:
      param = ‘qbbm‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = right_bl * Math.abs(list[i].position) + sdRightStartPosition - 2000
        y = 2250
        z = 8400
        roty = Math.PI
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition + 2000
        y = 2250
        z = -5900
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      sb.scale.set(0.3, 0.8, 1.05);
      group.add(sb)
      var textContent = d.displayContent ? d.displayContent : ‘暂无内容‘;
      qbbmSb = sb
      qbbmChangeState(textContent);
      break;
    case ‘FlexSpeedBroad‘:
      param = ‘kbxs‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;

      if (d.direction == ‘right‘) {
        x = right_bl * Math.abs(list[i].position) + sdRightStartPosition - 10000
        y = 1700
        z = 2500
        roty = Math.PI
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition + 10000
        y = 1700
        z = -2500
      }
      sb.position.copy(new THREE.Vector3(x, y, z));
      sb.rotateY(roty)
      group.add(sb)
      var number = d.displaySpeed ? d.displaySpeed : 0;
      if (d.direction == ‘right‘) {
        kbxsRightSb = sb
      }
      else {
        kbxsLeftSb = sb
      }
      kbxsChangeState(number, d.direction);
      break;
    case ‘PREmergencyCall‘:
      param = ‘jjdh‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = left_bl * Math.abs(list[i].position) + sdRightStartPosition
        y = 1800
        z = 14410
        roty = Math.PI
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition
        y = 1800
        z = -14410
      }
      if (d.position >= 610) {
        z = d.direction == ‘right‘ ? 17820 : -17820;
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      group.add(sb)
      break;

    case ‘VehicleDetector‘:
      param = ‘cljcq‘
      // 加上设备信息
      obj[param].userData = list[i]
      var d = list[i]
      var sb = obj[param].clone();
      var x, y, z;
      if (d.direction == ‘right‘) {
        x = left_bl * Math.abs(list[i].position) + sdRightStartPosition
        y = -2450
        z = 7300
        roty = -Math.PI / 2
      } else {
        x = left_bl * Math.abs(list[i].position) + sdLeftStartPosition
        y = -2450
        z = -7300
        roty = Math.PI / 2
      }
      sb.position.copy(new THREE.Vector3(x, y, z))
      sb.rotateY(roty)
      //sb.scale.set(10,10,10)
      group.add(sb)
      break;
    case ‘FireFightingDoor‘:
      param = ‘jlm‘
      // 加上设备信息
      obj[param].userData = list[i]
      var sb = obj[param].clone();
      var x = 0;
      var y = -100;
      var z = 0;
      sb.position.copy(new THREE.Vector3(x, y, z))
      group.add(sb)
      jlmChangeState(sb);
      break;
  }
}

}

}

function groupBy(array, f) {
if (array == undefined) {
return []
}
var groups = {};
array.forEach(function (o) {
var group = JSON.stringify(f(o));
groups[group] = groups[group] || [];
groups[group].push(o);
});
// console.log(‘groups‘,groups)
return Object.keys(groups).map(function (group) {
return groups[group];
});
}
function toSonArr(arr, i) {
arr.forEach(function (o) {
switch (o[0].classifyCode) {
case ‘1‘:
vueObj.$data[‘ventilationList_‘ + i] = o;
// 氮氧化物检测器...
break;
case ‘2‘:
vueObj.$data[‘lightList_‘ + i] = o;
// 照明集中控制器...
break;
case ‘3‘:
vueObj.$data[‘videomoniList_‘ + i] = o;
// 摄像头...
break;
case ‘4‘:
vueObj.$data[‘msgdeliverList_‘ + i] = o;
// 紧急电话广播...
break;
case ‘5‘:
vueObj.$data[‘firecontrolList_‘ + i] = o;
// 消防水泵...
break;
case ‘6‘:
vueObj.$data[‘traficcontrolList_‘ + i] = o;
// 车道指示器...
break;
case ‘7‘:
vueObj.$data[‘trafguideList_‘ + i] = o;
console.log(‘7:‘, o[0].name)
break;
case ‘8‘:
vueObj.$data[‘powerMoni_‘ + i] = o;
// 电力监测仪表...
break;
default:
//text = ‘List‘;
}
})

}

//动态化风机 //旋转方向
function fjRotate() {
if (fjArray.length === 0) return;
fjArray.forEach(item => {
fjdirection = item.userData.direction === ‘right‘ ? -1 : 1;
switch (item.userData.defaultCommandStatus) {
case ‘1‘??/正转
item.children[2].children[2].children[1].rotateX(fjdirection * Math.PI / 6);
item.children[2].children[2].children[2].rotateX(fjdirection * Math.PI / 6);
break;
case ‘2‘??/反转
item.children[2].children[2].children[1].rotateX(fjdirection * -Math.PI / 6);
item.children[2].children[2].children[2].rotateX(fjdirection * -Math.PI / 6);
break;
default:
break;
}
})
}

//车道指示器动态化
async function cdzsqChangeState(obj) {
if (obj.userData.typeCode !== ‘LaneLights‘) return;
groupSb.remove(obj);

var newobj = null;
switch (obj.userData.defaultCommandStatus) {
case ‘10‘:
//禁止通行状态
newobj = await cdzsqFactiNo();
newobj.userData = obj.userData;
newobj.position.copy(obj.position);
break;
case ‘6‘:
//反向状态
newobj = await cdzsqFacti();
newobj.scale.set(10, 10, 10);
newobj.userData = obj.userData;
newobj.position.copy(obj.position);
newobj.children[2].children[2].visible = true;
newobj.children[2].children[4].visible = true;
newobj.children[2].children[6].visible = true;
break;
case ‘9‘:
//正向状态
newobj = await cdzsqFacti();
newobj.scale.set(10, 10, 10);
newobj.rotateY(Math.PI)
newobj.userData = obj.userData;
newobj.position.copy(obj.position);
newobj.children[2].children[2].visible = true;
newobj.children[2].children[4].visible = true;
newobj.children[2].children[6].visible = true;
break;
default://默认关闭状态
newobj = await cdzsqFacti();
newobj.scale.set(10, 10, 10);
newobj.userData = obj.userData;
newobj.position.copy(obj.position);
newobj.children[2].children[2].visible = false;
newobj.children[2].children[4].visible = false;
newobj.children[2].children[6].visible = false;
break;
}
if (newobj.userData.direction === ‘left‘) newobj.rotateY(Math.PI)
groupSb.add(newobj)
//设置默认状态
newobj.children[2].children[9].visible = false;
newobj.children[2].children[10] && (newobj.children[2].children[10].visible = false);

}

//红绿灯动态化
function hldChangeState(obj) {
if (obj.userData.typeCode !== ‘TrafficLights‘) return;
switch (obj.userData.defaultCommandStatus) {
case ‘1‘:
//红灯状态
obj.children[0].children[4].children[0].visible = false;//绿灯
obj.children[0].children[4].children[1].visible = true;//绿灯灰色
obj.children[0].children[5].children[1].visible = false;//黄灯
obj.children[0].children[5].children[2].visible = true;//黄灯灰色
obj.children[0].children[6].children[1].visible = true;//红灯
obj.children[0].children[6].children[2].visible = false;//红灯灰色
obj.children[0].children[7].children[2].visible = false;//箭头
obj.children[0].children[7].children[1].visible = true;//箭头灰色
break;
case ‘2‘:
//黄灯状态
obj.children[0].children[4].children[0].visible = false;//绿灯
obj.children[0].children[4].children[1].visible = true;//绿灯灰色
obj.children[0].children[5].children[1].visible = true;//黄灯
obj.children[0].children[5].children[2].visible = false;//黄灯灰色
obj.children[0].children[6].children[1].visible = false;//红灯
obj.children[0].children[6].children[2].visible = true;//红灯灰色
obj.children[0].children[7].children[2].visible = false;//箭头
obj.children[0].children[7].children[1].visible = true;//箭头灰色
break;
case ‘4‘:
//绿灯状态
obj.children[0].children[4].children[0].visible = true;//绿灯
obj.children[0].children[4].children[1].visible = false;//绿灯灰色
obj.children[0].children[5].children[1].visible = false;//黄灯
obj.children[0].children[5].children[2].visible = true;//黄灯灰色
obj.children[0].children[6].children[1].visible = false;//红灯
obj.children[0].children[6].children[2].visible = true;//红灯灰色
obj.children[0].children[7].children[2].visible = false;//箭头
obj.children[0].children[7].children[1].visible = true;//箭头灰色
break;
case ‘8‘:
//左转状态
obj.children[0].children[4].children[0].visible = false;//绿灯
obj.children[0].children[4].children[1].visible = true;//绿灯灰色
obj.children[0].children[5].children[1].visible = false;//黄灯
obj.children[0].children[5].children[2].visible = true;//黄灯灰色
obj.children[0].children[6].children[1].visible = false;//红灯
obj.children[0].children[6].children[2].visible = true;//红灯灰色
obj.children[0].children[7].children[2].visible = true;//箭头
obj.children[0].children[7].children[1].visible = false;//箭头灰色
break;
default://默认停止状态 ‘0‘
obj.children[0].children[4].children[0].visible = false;//绿灯
obj.children[0].children[4].children[1].visible = true;//绿灯灰色
obj.children[0].children[5].children[1].visible = false;//黄灯
obj.children[0].children[5].children[2].visible = true;//黄灯灰色
obj.children[0].children[6].children[1].visible = false;//红灯
obj.children[0].children[6].children[2].visible = true;//红灯灰色
obj.children[0].children[7].children[2].visible = false;//箭头
obj.children[0].children[7].children[1].visible = true;//箭头灰色
break;
}
}

// //可变限速数值动态化
// export function kbxsChangeState(number,direction) {
// number = String(number)
// var speed=direction=‘right‘?speedRight:speedLeft;
// var kbxsSb=direction
=‘right‘?kbxsRightSb:kbxsLeftSb;
// if (speed) {
// speed.geometry.dispose();
// speed.material.dispose();
// kbxsSb.remove(speed);
// }
// textLoader.load(‘three/examples/fonts/gentilis_regular.typeface.json‘, function (font) {
// var geometry = new THREE.TextGeometry(number, {
// font: font,
// size: 250,
// height: 5,
// curveSegments: 12,
// bevelEnabled: true,
// bevelThickness: 10,
// bevelSize: 8,
// bevelSegments: 5
// });
// speed = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: 0xff0000 }));
// number.length === 2 ? speed.position.set(200, 780, 200) : (number.length === 3 ? speed.position.set(200, 780, 300) : speed.position.set(200, 780, 100))
// speed.rotation.set(0, Math.PI / 2, 0);
// kbxsSb.add(speed);
// });
// }

//可变限速数值动态化
export function kbxsChangeState(number, direction) {
number = String(number)
var speed = direction === ‘right‘ ? speedRight : speedLeft;
var kbxsSb = direction === ‘right‘ ? kbxsRightSb : kbxsLeftSb;
if (speed) {
speed.geometry.dispose();
speed.material.dispose();
kbxsSb.remove(speed);
}
function getTextCanvas(text) {
var width = 45, height = 45;
var canvas = document.createElement(‘canvas‘);
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext(‘2d‘);
ctx.fillStyle = ‘#000000‘;
ctx.fillRect(0, 0, width, height);
ctx.font = 40 + ‘px " bold‘;
ctx.fillStyle = ‘#00FF00‘;
ctx.textAlign = ‘center‘;
ctx.textBaseline = ‘middle‘;
ctx.fillText(text, width / 2, height / 2);
return canvas;
}
var geometry = new THREE.PlaneBufferGeometry(45, 45);
var material = new THREE.MeshBasicMaterial({ map: new THREE.CanvasTexture(getTextCanvas(‘60‘)) });// top
speed = new THREE.Mesh(geometry, material);
kbxsSb.add(speed)
speed.scale.set(10, 10, 10)
speed.position.set(200, 890, 0)
speed.rotateY(Math.PI / 2)

}

//可变信息板门架式文字动态化
export function qbbmChangeState(textContent) {
textContent = String(textContent);
//textContent = 道路千万条,安全第一条;行车不规范,亲人两行泪!一二三四五,上山打老虎;老虎不在家,就当我没说。
if (textM) {
textM.geometry.dispose();
textM.material.dispose();
qbbmSb.remove(textM);
}

var width = textContent.length * 50 + 200;
var height = 125;
function getTextCanvas(text) {
var canvas = document.createElement(‘canvas‘);
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext(‘2d‘);
ctx.fillStyle = ‘#050507‘;
ctx.fillRect(0, 0, width, height);
ctx.font = 50 + ‘px " bold‘;
ctx.fillStyle = ‘#FF0000‘;
ctx.textAlign = ‘center‘;
ctx.textBaseline = ‘middle‘;
ctx.fillText(text, width / 2, height / 2);
return canvas;
}
textMwidth = Number((1000 / width).toFixed(2));
var geometry = new THREE.PlaneGeometry(1020, height);
geometry.faceVertexUvs[0][0][0] = new THREE.Vector2(0, 1);
geometry.faceVertexUvs[0][0][1] = new THREE.Vector2(0, 0);
geometry.faceVertexUvs[0][0][2] = new THREE.Vector2(textMwidth, 1);
geometry.faceVertexUvs[0][1][0] = new THREE.Vector2(0, 0);
geometry.faceVertexUvs[0][1][1] = new THREE.Vector2(textMwidth, 0);
geometry.faceVertexUvs[0][1][2] = new THREE.Vector2(textMwidth, 1);
mapM = new THREE.CanvasTexture(getTextCanvas(textContent))

var material = new THREE.MeshBasicMaterial({ map: mapM });// top
textM = new THREE.Mesh(geometry, material);
qbbmSb.add(textM)
textM.scale.set(10, 10, 10)
textM.rotation.set(0, Math.PI / 2, 0)
mapM.wrapS = THREE.RepeatWrapping;//无限水平平铺
textM.position.set(1000, 1750, 0)
}

//可变信息板F式文字动态化
export function qbbfChangeState(textContent) {
if (textF) {
if (Array.isArray(textF)) {
textF.forEach(item => {
item.geometry.dispose();
item.material.dispose();
qbbfSb.remove(item);
})
}
else {
textF.geometry.dispose();
textF.material.dispose();
qbbfSb.remove(textF);
}
}
clearInterval(intervalF);
textF = [];
textContent = String(textContent);
var re = /[^\u4E00-\u9FA5]/g;
textContent = textContent.replace(re, ‘‘);
var index = 0;
for (let i = 0; i < Math.ceil(textContent.length / 4); i++) {
var str = textContent.substr(index, 4);
index += 4;
var width = 148;
var height = 45;
function getTextCanvas(text) {
var canvas = document.createElement(‘canvas‘);
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext(‘2d‘);
ctx.fillStyle = ‘#0A0A0A‘;
ctx.fillRect(0, 0, width, height);
ctx.font = 30 + ‘px " bold‘;
ctx.fillStyle = ‘#FF0000‘;
ctx.textAlign = ‘center‘;
ctx.textBaseline = ‘middle‘;
ctx.fillText(text, width / 2, height / 2);
return canvas;
}
var geometry = new THREE.PlaneGeometry(148, height);
mapF = new THREE.CanvasTexture(getTextCanvas(str))
var material = new THREE.MeshBasicMaterial({ map: mapF });// top
textF[i] = new THREE.Mesh(geometry, material);
qbbfSb.add(textF[i]);
textF[i].scale.set(10, 10, 10);
textF[i].rotation.set(0, -Math.PI / 2, 0);
if ((i + 1) % 2 === 1) {
textF[i].position.set(-100, 1500, -405);
}
else {
textF[i].position.set(-100, 1050, -405);
}
textF[i].visible = (i === 0 || i === 1) ? true : false;

}
var indexs = 2;
intervalF = setInterval(() => {
textF.forEach(item => {
item.visible = false;
})
textF[indexs] && (textF[indexs].visible = true);
textF[indexs + 1] && (textF[indexs + 1].visible = true);
if (indexs + 1 === textF.length || indexs + 2 === textF.length) {
indexs = 0
}
else {
indexs += 2;
}
}, 4000)

}

//卷帘门动态化
function jlmChangeState(obj) {
if (obj.userData.typeCode !== ‘FireFightingDoor‘) return;
switch (obj.userData.defaultCommandStatus) {
case "0"??/全开
obj.children[2].children[1].visible = false;//上
obj.children[2].children[2].visible = false;//下
break;
case "1"??/半开
obj.children[2].children[1].visible = true;//上
obj.children[2].children[2].visible = false;//下
break;
default:
obj.children[2].children[1].visible = true;//上
obj.children[2].children[2].visible = true;//下
break;
}
}

//修改颜色
export function colorChange(color, opacity) {
let object = scene.children.find(item => item.name === ‘dm‘)
object.children[14].material.color.set(color)
object.children[14].material.transparent = true;
object.children[14].material.opacity = opacity;

object.children[15].material.color.set(color)
object.children[15].material.transparent = true;
object.children[15].material.opacity = opacity;

object.children[16].material.color.set(color)
object.children[16].material.transparent = true;
object.children[16].material.opacity = opacity;

object.children[17].material.color.set(color)
object.children[17].material.transparent = true;
object.children[17].material.opacity = opacity;

object.children[158].children[0].material.color.set(color)
object.children[158].children[0].material.transparent = true;
object.children[158].children[0].material.opacity = opacity;
}

//情报板(门式)实现文字跑马灯效果
function fontRun() {
if (!textM) return;
mapM.offset.x += 0.001
}

three.js 记录

上一篇:微信小程序Map组件踩坑日记


下一篇:libcurl 错误CURLE_COULDNT_CONNECT 解决办法