|
@@ -0,0 +1,306 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="full" style="overflow: hidden">
|
|
|
|
|
+ <div class="full" id="3DScene"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+import * as Three from 'three'
|
|
|
|
|
+import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
|
|
|
|
|
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
|
|
|
+import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
|
|
|
|
|
+import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
|
|
|
|
|
+import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
|
|
|
|
|
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
|
|
|
|
+import Box1 from "../3DMap/map-components/infobox/box1";
|
|
|
|
|
+import InfoLayer from "../3DMap/map-components/InfoLayer";
|
|
|
|
|
+import MapForScene from "./map";
|
|
|
|
|
+import TWEEN from '@tweenjs/tween.js'
|
|
|
|
|
+import Box2 from "../3DMap/map-components/infobox/box2";
|
|
|
|
|
+import {getBox1, getCameraBox} from "./infoBox";
|
|
|
|
|
+import { createLogger } from 'vuex';
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ name: "Scene3D",
|
|
|
|
|
+ components: {Box2, MapForScene, InfoLayer, Box1},
|
|
|
|
|
+ provide(){
|
|
|
|
|
+ return {
|
|
|
|
|
+ scene: this
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ data(){
|
|
|
|
|
+ return{
|
|
|
|
|
+ raycaster: {},
|
|
|
|
|
+ mouse: {},
|
|
|
|
|
+ scene2D: {},
|
|
|
|
|
+ labelRenderer: {},
|
|
|
|
|
+ loadingSchedule: 0,
|
|
|
|
|
+ position: [0,0],
|
|
|
|
|
+ camera: null,
|
|
|
|
|
+ scene: null,
|
|
|
|
|
+ renderer: null,
|
|
|
|
|
+ mesh: null,
|
|
|
|
|
+ controls: null,
|
|
|
|
|
+ container: null,
|
|
|
|
|
+ tween: {},
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ props: {
|
|
|
|
|
+ cameraOption: {
|
|
|
|
|
+ type: "Object",
|
|
|
|
|
+ default: {
|
|
|
|
|
+ position: [1000,1000,1000],
|
|
|
|
|
+ near: 0.001,
|
|
|
|
|
+ far: 10000000,
|
|
|
|
|
+ lookAt: [0,0,0],
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ controlsOption: {
|
|
|
|
|
+ type: 'Object',
|
|
|
|
|
+ default: {
|
|
|
|
|
+ minDistance : 0,
|
|
|
|
|
+ maxDistance : 1000000,
|
|
|
|
|
+ autoRotate : false,
|
|
|
|
|
+ autoRotateSpeed : 1,
|
|
|
|
|
+ keyPanSpeed : 3,
|
|
|
|
|
+ target : new Three.Vector3(0,0,0),
|
|
|
|
|
+ enableDamping : true,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ //加载json文件
|
|
|
|
|
+ async addJson(url){
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ let loader = new Three.FileLoader();
|
|
|
|
|
+ loader.load(url, (data) => {
|
|
|
|
|
+ resolve( JSON.parse(data));
|
|
|
|
|
+ });
|
|
|
|
|
+ } )
|
|
|
|
|
+ },
|
|
|
|
|
+ async addObj(mtlUrl,objUrl,handle,process){
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ let loader = new OBJLoader();
|
|
|
|
|
+ let mtlLoader = new MTLLoader();
|
|
|
|
|
+ mtlLoader.load(mtlUrl, (material) => {
|
|
|
|
|
+ loader.setMaterials(material);
|
|
|
|
|
+ loader.load(objUrl, (loadedMesh) => {
|
|
|
|
|
+ handle(loadedMesh);
|
|
|
|
|
+ },(xhr)=>{
|
|
|
|
|
+ process(xhr);
|
|
|
|
|
+ if((Math.floor((xhr.loaded/xhr.total)*1000)*0.1+'').substring(0,4)=='100'){
|
|
|
|
|
+ resolve();
|
|
|
|
|
+ }
|
|
|
|
|
+ },(err)=>{
|
|
|
|
|
+ reject(err)});
|
|
|
|
|
+ });
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ addHDR(url){
|
|
|
|
|
+ const pmremGenerator = new Three.PMREMGenerator(this.renderer); // 使用hdr作为背景色
|
|
|
|
|
+ pmremGenerator.compileEquirectangularShader();
|
|
|
|
|
+ const scene = this.scene;
|
|
|
|
|
+ new RGBELoader()
|
|
|
|
|
+ .setDataType(Three.UnsignedByteType)
|
|
|
|
|
+ .load(url, function (texture) {
|
|
|
|
|
+ const envMap = pmremGenerator.fromEquirectangular(texture).texture;
|
|
|
|
|
+ // envMap.isPmremTexture = true;
|
|
|
|
|
+ pmremGenerator.dispose();
|
|
|
|
|
+
|
|
|
|
|
+ scene.environment = envMap; // 给场景添加环境光效果
|
|
|
|
|
+ scene.background = envMap; // 给场景添加背景图
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ //添加天空盒子===》urls 6张方位图图片路径 //六张图片分别是朝前的(posz)、朝后的(negz)、朝上的(posy)、朝下的(negy)、朝右的(posx)和朝左的(negx)。
|
|
|
|
|
+ addSkyBox(urls){
|
|
|
|
|
+ let cubeTextureLoader = new Three.CubeTextureLoader();
|
|
|
|
|
+ let cubeTexture = cubeTextureLoader.load( urls);
|
|
|
|
|
+ this.scene.background = cubeTexture;
|
|
|
|
|
+ },
|
|
|
|
|
+ //添加平行光
|
|
|
|
|
+ addDirectionalLight(position,color=0xffffff){
|
|
|
|
|
+ //添加光源
|
|
|
|
|
+ let dirLight = new Three.DirectionalLight(color, 1)
|
|
|
|
|
+ dirLight.position.set(position[0], position[1], position[2]);
|
|
|
|
|
+ dirLight.castShadow = true //可以产生阴影
|
|
|
|
|
+ dirLight.shadow.mapSize = new Three.Vector2(1024, 1024);
|
|
|
|
|
+ dirLight.onlyShadow = true;
|
|
|
|
|
+ dirLight.shadow.camera.near = 200;
|
|
|
|
|
+ dirLight.shadow.camera.far = 1000;
|
|
|
|
|
+
|
|
|
|
|
+ this.scene.add(dirLight)
|
|
|
|
|
+ this.scene.add(dirLight.target)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //三维坐标转换屏幕坐标
|
|
|
|
|
+ transPosition(position=[0,0,0]){
|
|
|
|
|
+ let world_vector = new Three.Vector3(position[0],position[1],position[2]);
|
|
|
|
|
+ let vector =world_vector.project(this.camera);
|
|
|
|
|
+ let halfWidth = window.innerWidth / 2,
|
|
|
|
|
+ halfHeight = window.innerHeight / 2;
|
|
|
|
|
+ return [
|
|
|
|
|
+ Math.round(vector.x * halfWidth + halfWidth),
|
|
|
|
|
+ Math.round(-vector.y * halfHeight + halfHeight)
|
|
|
|
|
+ ];
|
|
|
|
|
+ },
|
|
|
|
|
+ //添加gltf模型
|
|
|
|
|
+ addGltf(url,handleGLTF,handleProgress){
|
|
|
|
|
+ let loader = new GLTFLoader();
|
|
|
|
|
+ loader.castShadow =true;
|
|
|
|
|
+ loader.receiveShadow = true;
|
|
|
|
|
+ loader.load( url, (gltf) => {
|
|
|
|
|
+ handleGLTF(gltf);
|
|
|
|
|
+ }, (xhr)=> {
|
|
|
|
|
+ handleProgress(xhr)
|
|
|
|
|
+ },
|
|
|
|
|
+ ( error ) => {
|
|
|
|
|
+ alert(error);
|
|
|
|
|
+ } );
|
|
|
|
|
+ },
|
|
|
|
|
+ //初始化场景
|
|
|
|
|
+ async init() {
|
|
|
|
|
+
|
|
|
|
|
+ //挂载容器
|
|
|
|
|
+ let container = document.getElementById('3DScene');
|
|
|
|
|
+
|
|
|
|
|
+ this.raycaster = new Three.Raycaster();
|
|
|
|
|
+
|
|
|
|
|
+ //初始化相机
|
|
|
|
|
+ this.camera = new Three.PerspectiveCamera(50, container.clientWidth/container.clientHeight, this.cameraOption.near, this.cameraOption.far);
|
|
|
|
|
+ this.camera.position.z = this.cameraOption.position[0];
|
|
|
|
|
+ this.camera.position.x = this.cameraOption.position[1];
|
|
|
|
|
+ this.camera.position.y = this.cameraOption.position[2];
|
|
|
|
|
+ this.camera.lookAt(...this.cameraOption.lookAt);
|
|
|
|
|
+
|
|
|
|
|
+ //初始化场景
|
|
|
|
|
+ this.scene = new Three.Scene();
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ //渲染器
|
|
|
|
|
+ this.renderer = new Three.WebGLRenderer({antialias: true,alpha: true,logarithmicDepthBuffer: true});
|
|
|
|
|
+ this.renderer.setSize(container.clientWidth, container.clientHeight);
|
|
|
|
|
+ this.renderer.shadowMap.enabled = true;
|
|
|
|
|
+ this.renderer.shadowMap.type = Three.PCFSoftShadowMap;
|
|
|
|
|
+ container.appendChild(this.renderer.domElement);
|
|
|
|
|
+
|
|
|
|
|
+ this.labelRenderer = new CSS2DRenderer();
|
|
|
|
|
+ this.labelRenderer.setSize( container.clientWidth, container.clientHeight);
|
|
|
|
|
+ this.labelRenderer.domElement.style.position = 'absolute';
|
|
|
|
|
+ this.labelRenderer.domElement.style.top = '0px';
|
|
|
|
|
+ container.appendChild( this.labelRenderer.domElement );
|
|
|
|
|
+
|
|
|
|
|
+ //坐标轴
|
|
|
|
|
+ // let axes = new Three.AxisHelper(30000);
|
|
|
|
|
+ // this.scene.add(axes);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ //半球光照
|
|
|
|
|
+ this.scene.add(new Three.HemisphereLight(0xffffff,0xffffff,0.45));
|
|
|
|
|
+
|
|
|
|
|
+ //控制器
|
|
|
|
|
+ this.controls = new OrbitControls(this.camera, this.labelRenderer.domElement);
|
|
|
|
|
+ this.controls.minDistance = this.controlsOption.minDistance;
|
|
|
|
|
+ this.controls.maxDistance = this.controlsOption.maxDistance;
|
|
|
|
|
+ this.controls.autoRotate = this.controlsOption.autoRotate;
|
|
|
|
|
+ this.controls.autoRotateSpeed = this.controlsOption.autoRotateSpeed;
|
|
|
|
|
+ this.controls.keyPanSpeed = this.controlsOption.keyPanSpeed;
|
|
|
|
|
+ this.controls.target = this.controlsOption.target;
|
|
|
|
|
+ this.controls.enableDamping = this.controlsOption.enableDamping;
|
|
|
|
|
+ this.controls.rotateSpeed = 0.5;
|
|
|
|
|
+ this.controls.maxPolarAngle = 0.49 * Math.PI;
|
|
|
|
|
+ this.controls.enableDamping = this.controlsOption.enableDamping;
|
|
|
|
|
+ this.controls.zoomSpeed = 0.5;
|
|
|
|
|
+ this.controls.panSpeed = 0.5;
|
|
|
|
|
+ this.controls.enablePan = true;
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener( 'mousemove', this.onMouseMove, false );
|
|
|
|
|
+ window.addEventListener( 'click', this.onMouseClick, false );
|
|
|
|
|
+ this.$emit('loadingFinish',{});
|
|
|
|
|
+ },
|
|
|
|
|
+ //每帧渲染函数
|
|
|
|
|
+ animate() {
|
|
|
|
|
+ requestAnimationFrame(this.animate);
|
|
|
|
|
+ this.controls.update();
|
|
|
|
|
+ this.renderer.render(this.scene, this.camera);
|
|
|
|
|
+ this.labelRenderer.render( this.scene, this.camera );
|
|
|
|
|
+ TWEEN.update();
|
|
|
|
|
+ },
|
|
|
|
|
+ //设置背景颜色
|
|
|
|
|
+ setBackground(color){
|
|
|
|
|
+ this.renderer.setClearColor(color);
|
|
|
|
|
+ },
|
|
|
|
|
+ //设置背景透明度
|
|
|
|
|
+ setAlpha(num){
|
|
|
|
|
+ this.renderer.setClearAlpha(num);
|
|
|
|
|
+ },
|
|
|
|
|
+ //窗口大小监听
|
|
|
|
|
+ resize(resizeHandle){
|
|
|
|
|
+ let width = window.innerWidth;
|
|
|
|
|
+ let height = window.innerHeight;
|
|
|
|
|
+ this.camera.aspect = width / height;
|
|
|
|
|
+ this.camera.updateProjectionMatrix();
|
|
|
|
|
+ this.renderer.setSize(width, height);
|
|
|
|
|
+ this.labelRenderer.setSize( width, height);
|
|
|
|
|
+ },
|
|
|
|
|
+ //定位相机视角
|
|
|
|
|
+
|
|
|
|
|
+ //鼠标移动事件
|
|
|
|
|
+ onMouseMove(event) {
|
|
|
|
|
+ const _this =this;
|
|
|
|
|
+ // _this.mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
|
|
|
|
|
+ // _this.mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
|
|
|
|
|
+ // _this.eventOffset.x = event.clientX;
|
|
|
|
|
+ // _this.eventOffset.y = event.clientY;
|
|
|
|
|
+ // _this.infoPosition.x = _this.eventOffset.x + 2 ;
|
|
|
|
|
+ // _this.infoPosition.y = _this.eventOffset.y + 2 ;
|
|
|
|
|
+ },
|
|
|
|
|
+ //鼠标单击事件
|
|
|
|
|
+ onMouseClick(){
|
|
|
|
|
+ // alert(1);
|
|
|
|
|
+ },
|
|
|
|
|
+ //漫游视角到指定位置
|
|
|
|
|
+ flyto(to,from=this.camera.position,time=1000){
|
|
|
|
|
+ let _this = this;
|
|
|
|
|
+ const {x,y,z} = to;
|
|
|
|
|
+ console.log(this.controls,123456);
|
|
|
|
|
+ this.tween = new TWEEN.Tween(from)
|
|
|
|
|
+ .to({x:x+50,y:y+50,z:z+50},time)
|
|
|
|
|
+ .easing(TWEEN.Easing.Quadratic.InOut);
|
|
|
|
|
+
|
|
|
|
|
+ this.tween.onUpdate(function(){
|
|
|
|
|
+ _this.controls.target.set(x,y,z);
|
|
|
|
|
+ })
|
|
|
|
|
+ this.tween.onComplete(()=>{
|
|
|
|
|
+ this.camera.lookAt(to)
|
|
|
|
|
+ })
|
|
|
|
|
+ this.tween.start();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async mounted() {
|
|
|
|
|
+ const _this = this;
|
|
|
|
|
+ await this.init();
|
|
|
|
|
+ this.animate();
|
|
|
|
|
+ window.addEventListener('resize', this.resize);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+ .full{
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ background: #dddddd;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .center{
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ }
|
|
|
|
|
+ @keyframes fade-in {
|
|
|
|
|
+ from{
|
|
|
|
|
+ opacity: 0.3;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+</style>
|