|
@@ -0,0 +1,692 @@
|
|
|
+<template src="./webgl_rxdz_vr.html">
|
|
|
+
|
|
|
+</template>
|
|
|
+<script>
|
|
|
+ import * as THREE from 'three';
|
|
|
+ import Stats from 'three/addons/libs/stats.module.js';
|
|
|
+ import {
|
|
|
+ OrbitControls
|
|
|
+ } from 'three/addons/controls/OrbitControls.js';
|
|
|
+ import {
|
|
|
+ GLTFLoader
|
|
|
+ } from 'three/addons/loaders/GLTFLoader.js';
|
|
|
+
|
|
|
+ import TWEEN from 'three/addons/libs/tween.module.js';
|
|
|
+ import {
|
|
|
+ getStorage
|
|
|
+ } from '@/utils/localStorage';
|
|
|
+ var requestId = "";
|
|
|
+ const util = require('@/utils/util.js').default;
|
|
|
+ // import util from '@/utils/util.js';
|
|
|
+ // const config = require('@/services/urlConfig.js');
|
|
|
+ // import requestConfig from '@/static/lib/requestConfig.js';
|
|
|
+ import viewShell from '@/components/newBottomCom/viewShell/viewShell.vue';
|
|
|
+ import viewMask from '@/components/newBottomCom/viewMask/viewMask.vue';
|
|
|
+
|
|
|
+ // import { RGBELoader } from '@/webgl/jsm/loaders/RGBELoader.js';
|
|
|
+ import screenshot from '@/mixins/screenshot.js';
|
|
|
+ import floorMethod from '@/mixins/floorMethod.js';
|
|
|
+ import loadModel from '@/mixins/loadModel.js';
|
|
|
+ import wallMethod from '@/mixins/wallMethod.js';
|
|
|
+ import commonPageMethod from '@/mixins/commonPageMethod.js';
|
|
|
+ // import commonPageMethod from '@/common/commonPageMethod.js';
|
|
|
+ // const app = getApp(); //获取应用实例
|
|
|
+ export default {
|
|
|
+ components: {
|
|
|
+ viewShell,
|
|
|
+ viewMask
|
|
|
+ },
|
|
|
+ mixins: [screenshot, loadModel, floorMethod, wallMethod, commonPageMethod],
|
|
|
+ /**
|
|
|
+ * 页面的初始数据
|
|
|
+ */
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ pvCurPageName: "room_show",
|
|
|
+ locusBehaviorName: "房间展示",
|
|
|
+ pvCurPageParams: null,
|
|
|
+ houseId: "",
|
|
|
+ pvId: 'p_2cmina_23080402',
|
|
|
+ canvas: null,
|
|
|
+ navbar: {
|
|
|
+ showCapsule: 1,
|
|
|
+ title: '客厅',
|
|
|
+ titleColor: '#000',
|
|
|
+ navPadding: 0,
|
|
|
+ navPaddingBg: 'transparent',
|
|
|
+ navBarColor: 'transparent',
|
|
|
+ navBackColor: 'transparent',
|
|
|
+ haveCallback: true, // 如果是 true 会接手 navbarBackClk
|
|
|
+ fromShare: false,
|
|
|
+ fromProject: 0,
|
|
|
+ shareToken: "",
|
|
|
+ pageName: this.pvCurPageName,
|
|
|
+ },
|
|
|
+ id: '', // 户型编号
|
|
|
+ spaceList: [], // 空间列表
|
|
|
+ gltfSpaces: [], // 场景中地板模型数组
|
|
|
+ gltfSpaceRoofs: [],
|
|
|
+ curSpaceObj: null, // 当前选中的空间
|
|
|
+ // curSpaceIndex:-1, // 当前选中的空间索引
|
|
|
+ spaceId: null,
|
|
|
+ wallIds: [], // 空间墙体id
|
|
|
+ // wallList:[], // 墙体数据
|
|
|
+ gltfWalls: [], // 场景中墙体模型数组
|
|
|
+ loader: null,
|
|
|
+ scene: null,
|
|
|
+ // sky:null,
|
|
|
+ camera: null,
|
|
|
+ controlStarPosition: {
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ z: 0
|
|
|
+ }, //控制器初始位置
|
|
|
+ cameraStarPosition: {
|
|
|
+ x: 0,
|
|
|
+ y: 20,
|
|
|
+ z: 0
|
|
|
+ }, //摄像头初始位置
|
|
|
+ cameraLastPosition: null, //摄像头上一次移动到的位置
|
|
|
+ controlLastPosition: null, //观察点上一次移动到的位置
|
|
|
+ canvasHeight: 408, //canvas视图的高度-计算得出
|
|
|
+ chooseMesh: null, //标记鼠标拾取到的mesh
|
|
|
+ shottingImg: [],
|
|
|
+ progress: 1, //进度条
|
|
|
+ myLoadingStatus: false,
|
|
|
+ // textGeoList:[],
|
|
|
+ repeatFlag: false, //重复点击
|
|
|
+ // skyPlan: null, // 天空盒子
|
|
|
+ instancedMeshList: [],
|
|
|
+ screenshotResolve: null,
|
|
|
+ actors: [],
|
|
|
+ showDownView: true, //默认显示下载按钮
|
|
|
+ currentActor: null,
|
|
|
+ circleGroup: null, //圆形地标
|
|
|
+ isIOS: false,
|
|
|
+ defaulIndex: null, //默认视角的序号
|
|
|
+ // aiImagesList:[
|
|
|
+ // // "https://dm.static.elab-plus.com/miniProgram/plus_IM01.png",
|
|
|
+ // // "https://dm.static.elab-plus.com/miniProgram/plus_IM02.png",
|
|
|
+ // // "https://dm.static.elab-plus.com/miniProgram/plus_IM03.png",
|
|
|
+ // // "https://dm.static.elab-plus.com/miniProgram/plus_IM04.png",
|
|
|
+ // ]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ cancelAnimationFrame(requestId, this.canvas)
|
|
|
+ this.worker && this.worker.terminate()
|
|
|
+ if (this.renderer instanceof THREE.WebGLRenderer) {
|
|
|
+ // 遍历场景中的所有子对象,找到类型为Mesh的对象并移除
|
|
|
+ let deleList = this.scene.children.filter(object => {
|
|
|
+ if (object instanceof THREE.Mesh) {
|
|
|
+ return object
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (deleList && deleList.length > 0) {
|
|
|
+ this.scene.remove(...deleList);
|
|
|
+ }
|
|
|
+ this.scene.traverse(function(object) {
|
|
|
+ if (object instanceof THREE.Mesh) {
|
|
|
+ if (object.geometry && typeof(object.geometry.dispose) == 'function') {
|
|
|
+ object.geometry.dispose();
|
|
|
+ }
|
|
|
+ if (object.material && typeof(object.material.dispose) == 'function') {
|
|
|
+ object.material.dispose();
|
|
|
+ }
|
|
|
+ if (object.texture && typeof(object.texture.dispose) == 'function') {
|
|
|
+ object.texture.dispose();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.renderer.clear();
|
|
|
+ this.renderer.dispose();
|
|
|
+ this.renderer.forceContextLoss();
|
|
|
+ this.renderer.context = null;
|
|
|
+ this.renderer.domElement = null;
|
|
|
+ this.renderer = null;;
|
|
|
+ this.clearHandle()
|
|
|
+ }
|
|
|
+ TWEEN && TWEEN.removeAll(); //清除所有的tween;
|
|
|
+ console.warn("***beforeDestroy-webgl_rxdz_roam***");
|
|
|
+ },
|
|
|
+ mounted(options) {
|
|
|
+ var that = this;
|
|
|
+ // alert("JavaScript 堆大小限制: "+performance.memory.jsHeapSizeLimit
|
|
|
+ // +"\n已使用的 JavaScript 堆大小: "+performance.memory.usedJSHeapSize
|
|
|
+ // +"\nJavaScript 堆的总大小: "+performance.memory.totalJSHeapSize);
|
|
|
+ console.warn("***webgl_rxdz_roam-options***", this.$route.query)
|
|
|
+ this.isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
|
|
|
+
|
|
|
+ // alert("***mounted-webgl_rxdz_roam***"+this.isIOS + this.curHouseObj)
|
|
|
+ let screenWidth = window.screen.width;
|
|
|
+ let screenHeight = window.screen.height;
|
|
|
+ if (window.innerWidth && window.screen.width) {
|
|
|
+ screenWidth = Math.min(window.innerWidth, window.screen.width)
|
|
|
+ }
|
|
|
+ if (window.innerHeight && window.screen.height) {
|
|
|
+ screenHeight = Math.min(window.innerHeight, window.screen.height)
|
|
|
+ }
|
|
|
+ let unit = screenWidth / 750; //单位rpm 对应 px 的值
|
|
|
+ this.canvasHeight = screenHeight - (600 * unit) + (40 * unit);
|
|
|
+ this.houseId = this.$route.query.houseId ? this.$route.query.houseId : '';
|
|
|
+ this.spaceId = this.$route.query.spaceId ? this.$route.query.spaceId : '';
|
|
|
+ let container = this.$refs.webgl;
|
|
|
+ let canvas3d = this.canvas = this.$refs.glcanvas;
|
|
|
+ //uniapp 兼容写法,因为uni的页面对象的Vue 实例是$vm
|
|
|
+
|
|
|
+ let camera = null,
|
|
|
+ renderer = null;
|
|
|
+ let needRender = false; //是否需要渲染 false表示不需要渲染;true 表示需要渲染
|
|
|
+ let loader = this.loader = new GLTFLoader();
|
|
|
+ let scene = this.scene = new THREE.Scene();
|
|
|
+ let raycaster = null;
|
|
|
+ let mouse = new THREE.Vector2();
|
|
|
+ let chooseMesh = this.chooseMesh; //标记鼠标拾取到的mesh
|
|
|
+ let isUserContorl = true; //是否进入漫游状态-默认是
|
|
|
+ //漫游时变量
|
|
|
+ let onPointerDownMouseX = 0,
|
|
|
+ onPointerDownMouseY = 0,
|
|
|
+ lon = 0;
|
|
|
+ let fingerCount = 0; //触摸时的手指数目
|
|
|
+ let startTime = 0; //非漫游时的移动变量
|
|
|
+ let tweenCameraAnma = false; //表示当前是否处在动画过程中
|
|
|
+ let controls = null,
|
|
|
+ boundary = null;
|
|
|
+ let stats;
|
|
|
+ init();
|
|
|
+ // this.$refs.myLoading.showLoading("加载中...1%")
|
|
|
+ // this.myLoadingStatus = true;
|
|
|
+ this.clearEvent = clearEvent;
|
|
|
+ this.attendEvent = attendEvent;
|
|
|
+ this.tweenCameraAnmaChange = tweenCameraAnmaChange;
|
|
|
+ this.starRender = starRender; //对外暴露启动渲染的方法
|
|
|
+ this.stopRender = stopRender; //对外暴露停止渲染的方法
|
|
|
+
|
|
|
+ function init() {
|
|
|
+
|
|
|
+ // scene.background = new THREE.Color("#FFFFFF");
|
|
|
+ // scene.environment = new THREE.Color("#F2F2F2");
|
|
|
+ // 创建一个HDR贴图加载器
|
|
|
+ // const rgbeloader = new RGBELoader();
|
|
|
+ // // 加载HDR贴图
|
|
|
+ // rgbeloader.load('https://dm.static.elab-plus.com/miniProgram/environment.hdr', (texture) => {
|
|
|
+ // // 将HDR贴图设置为场景的环境贴图
|
|
|
+ // texture.mapping = THREE.EquirectangularReflectionMapping;
|
|
|
+ // scene.environment = texture;
|
|
|
+ // })
|
|
|
+
|
|
|
+ // 创建相机位置
|
|
|
+ camera = new THREE.PerspectiveCamera(90, screenWidth / that.canvasHeight, 0.1, 10000);
|
|
|
+ // camera.up.set(0, 1, 0);//俯视状态,将相机的up向量设置为z轴负方向 {x:0,y:1,z:0}
|
|
|
+ camera.position.set(that.cameraStarPosition.x, that.cameraStarPosition.y, that.cameraStarPosition.z);
|
|
|
+ scene.add(camera);
|
|
|
+ that.camera = camera;
|
|
|
+
|
|
|
+ // 环境光会均匀的照亮场景中的所有物体
|
|
|
+ const ambientLight = new THREE.AmbientLight(0xFFEFE0, 3.5);
|
|
|
+ scene.add(ambientLight);
|
|
|
+
|
|
|
+ const loader = new THREE.TextureLoader();
|
|
|
+ const texture = loader.load(
|
|
|
+ 'https://dm.static.elab-plus.com/miniProgram/tears_of_steel_bridge_2k.jpg', () => {
|
|
|
+ const rt = new THREE.WebGLCubeRenderTarget(texture.image.height);
|
|
|
+ rt.fromEquirectangularTexture(renderer, texture);
|
|
|
+ scene.background = rt.texture;
|
|
|
+ });
|
|
|
+ //antialias 这个值得设置为false,不然IOS上截图会失效
|
|
|
+ renderer = that.renderer = new THREE.WebGLRenderer({
|
|
|
+ canvas: canvas3d,
|
|
|
+ alpha: true,
|
|
|
+ antialias: true,
|
|
|
+ preserveDrawingBuffer: true,
|
|
|
+ });
|
|
|
+ if (!that.isIOS) {
|
|
|
+ renderer.shadowMap.enabled = true; //产生阴影
|
|
|
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 阴影属性
|
|
|
+ }
|
|
|
+ renderer.outputEncoding = THREE.sRGBEncoding;
|
|
|
+ renderer.outputColorSpace = THREE.SRGBColorSpace;
|
|
|
+ // renderer.toneMappingExposure = 0.1;//色调映射的曝光级别。默认是1
|
|
|
+ renderer.toneMapping = THREE.NoToneMapping; //色调映射
|
|
|
+ renderer.physicallyCorrectLights = true; //关键参数,模拟物理光照影响,必须设置为true
|
|
|
+
|
|
|
+ renderer.setPixelRatio(window.devicePixelRatio);
|
|
|
+ renderer.setSize(screenWidth, that.canvasHeight);
|
|
|
+ container.appendChild(renderer.domElement);
|
|
|
+
|
|
|
+ controls = new OrbitControls(camera, renderer.domElement);
|
|
|
+ controls.enableDamping = true; //启动缓动
|
|
|
+ controls.minDistance = 0.0001;
|
|
|
+ controls.maxDistance = 10;
|
|
|
+ controls.minPolarAngle = 0; // 默认0
|
|
|
+ controls.maxPolarAngle = Math.PI / 2; // 默认Math.PI,即可以向下旋转到的视角。
|
|
|
+ // controls.target.set( that.controlStarPosition.x, that.controlStarPosition.y, that.controlStarPosition.z);
|
|
|
+ controls.enableZoom = true; //启用摄像机的缩放
|
|
|
+ controls.enablePan = true; //禁用摄像机平移
|
|
|
+ controls.enableRotate = true; //启用摄像机水平或垂直旋转
|
|
|
+ // controls.zoomToCursor = true;
|
|
|
+
|
|
|
+ // controls.target.copy(camera.position);
|
|
|
+ // controls.update();
|
|
|
+ // 监听相机移动事件-限制只能在当前空间范围内移动
|
|
|
+ controls.addEventListener('change', () => {
|
|
|
+ // 检查相机位置是否超出边界框
|
|
|
+ if (boundary && !boundary.containsPoint(camera.position)) {
|
|
|
+ let clampedPosition = new THREE.Vector3();
|
|
|
+ boundary.clampPoint(camera.position, clampedPosition);
|
|
|
+ if (clampedPosition) {
|
|
|
+ camera.position.copy(clampedPosition);
|
|
|
+ // controls.target.copy(clampedPosition);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // controls.target = new THREE.Vector3( );;
|
|
|
+ camera.lookAt(that.controlStarPosition.x, that.controlStarPosition.y, that.controlStarPosition.z);
|
|
|
+ raycaster = new THREE.Raycaster();
|
|
|
+ // stats = new Stats();
|
|
|
+ // container.appendChild(stats.dom);
|
|
|
+ // stats.domElement.style.top = '100px';
|
|
|
+ attendEvent(); //注册监听事件
|
|
|
+ starRender(); //启动渲染
|
|
|
+ setTimeout(() => {
|
|
|
+ videoHandle()
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+
|
|
|
+ function videoHandle() {
|
|
|
+ // let video = document.getElementById('video');
|
|
|
+
|
|
|
+ // let texture = new THREE.VideoTexture(video);
|
|
|
+ // texture.colorSpace = THREE.SRGBColorSpace;
|
|
|
+
|
|
|
+ // 创建视频元素
|
|
|
+ var video = document.createElement('video');
|
|
|
+ video.src = 'https://dm.static.elab-plus.com/miniProgram/green.mp4'; // 视频文件的路径
|
|
|
+ // video.crossOrigin = "anonymous";this.crossOrigin = 'anonymous';
|
|
|
+ video.setAttribute("crossOrigin", "Anonymous");
|
|
|
+ video.loop = true;
|
|
|
+ video.muted = true;
|
|
|
+ // video.load();
|
|
|
+ video.play();
|
|
|
+ // 创建视频纹理
|
|
|
+ var texture = new THREE.VideoTexture(video);
|
|
|
+ // texture.colorSpace = THREE.SRGBColorSpace;
|
|
|
+ // texture.minFilter = THREE.LinearFilter;
|
|
|
+ // texture.magFilter = THREE.LinearFilter;
|
|
|
+ // texture.format = THREE.RGBFormat;
|
|
|
+
|
|
|
+ // 创建绿幕着色器
|
|
|
+ let greenScreenShader = {
|
|
|
+ uniforms: {
|
|
|
+ pointTexture: {
|
|
|
+ value: texture
|
|
|
+ },
|
|
|
+ },
|
|
|
+ vertexShader: `
|
|
|
+ varying vec2 vUv;
|
|
|
+ void main() {
|
|
|
+ vUv = uv;
|
|
|
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
|
+ } `,
|
|
|
+ fragmentShader: `
|
|
|
+ uniform sampler2D pointTexture;
|
|
|
+ varying vec2 vUv;
|
|
|
+ void main() {
|
|
|
+ vec4 color = texture2D(pointTexture, vUv);
|
|
|
+ float threshold = 0.5;
|
|
|
+ if (color.g > threshold) {
|
|
|
+ discard;
|
|
|
+ } else {
|
|
|
+ gl_FragColor = color;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ `,
|
|
|
+
|
|
|
+ // fragmentShader: `
|
|
|
+ // uniform sampler2D pointTexture;
|
|
|
+ // uniform vec3 backColor;
|
|
|
+ // varying vec2 vUv;
|
|
|
+ // uniform float u_threshold;
|
|
|
+ // float u_clipBlack = 0.2;
|
|
|
+ // float u_clipWhite = 4.0;
|
|
|
+
|
|
|
+ // float rgb2cb(float r, float g, float b){ return 0.5 + -0.168736*r - 0.331264*g + 0.5*b; }
|
|
|
+ // float rgb2cr(float r, float g, float b){ return 0.5 + 0.5*r - 0.418688*g - 0.081312*b; }
|
|
|
+ // float smoothclip(float low, float high, float x){ if (x <= low){ return 0.0; } if(x >= high){ return 1.0; } return (x-low)/(high-low); }
|
|
|
+ // vec4 greenscreen(vec4 colora, float Cb_key,float Cr_key, float tola,float tolb, float clipBlack, float clipWhite)
|
|
|
+ // {
|
|
|
+ // float cb = rgb2cb(colora.r,colora.g,colora.b);
|
|
|
+ // float cr = rgb2cr(colora.r,colora.g,colora.b);
|
|
|
+ // float alpha = distance(vec2(cb, cr), vec2(Cb_key, Cr_key));
|
|
|
+ // alpha = smoothclip(tola, tolb, alpha);
|
|
|
+ // float r = max(gl_FragColor.r - (1.0-alpha)*backColor.r, 0.0);
|
|
|
+ // float g = max(gl_FragColor.g - (1.0-alpha)*backColor.g, 0.0);
|
|
|
+ // float b = max(gl_FragColor.b - (1.0-alpha)*backColor.b, 0.0);
|
|
|
+ // if(alpha < clipBlack){ alpha = r = g = b = 0.0; }
|
|
|
+ // if(alpha > clipWhite){ alpha = 1.0; }
|
|
|
+ // if(clipWhite < 1.0){ alpha = alpha/max(clipWhite, 0.9); }
|
|
|
+ // return vec4(r,g,b, alpha);
|
|
|
+ // }
|
|
|
+ // void main( void ) {
|
|
|
+
|
|
|
+ // gl_FragColor = vec4(texture2D(pointTexture, vUv).rgb, 1);
|
|
|
+
|
|
|
+ // float tola = 0.0;
|
|
|
+ // float tolb = u_threshold/2.0;
|
|
|
+ // float cb_key = rgb2cb(backColor.r, backColor.g, backColor.b);
|
|
|
+ // float cr_key = rgb2cr(backColor.r, backColor.g, backColor.b);
|
|
|
+ // gl_FragColor = greenscreen(gl_FragColor, cb_key, cr_key, tola, tolb, u_clipBlack, u_clipWhite);
|
|
|
+
|
|
|
+ // }
|
|
|
+ // `,
|
|
|
+
|
|
|
+ };
|
|
|
+ // 创建绿幕材质
|
|
|
+ let greenScreenMaterial = new THREE.ShaderMaterial(greenScreenShader);
|
|
|
+
|
|
|
+ let geometry = new THREE.PlaneGeometry(16, 9);
|
|
|
+ // var geometry = new THREE.BoxGeometry(9, 9, 9);
|
|
|
+ // geometry.scale(0.5, 0.5, 0.5);
|
|
|
+ // 应用绿幕材质到对象上
|
|
|
+ let mesh = new THREE.Mesh(geometry, greenScreenMaterial);
|
|
|
+ mesh.position.set(0, 0, 0);
|
|
|
+ // mesh.rotation.x = -Math.PI / 2 ; // 旋转 180 度
|
|
|
+ // scene.add(object);
|
|
|
+ // const mesh = new THREE.Mesh(geometry, material);
|
|
|
+ // mesh.position.setFromSphericalCoords(radius, phi, theta);
|
|
|
+ mesh.lookAt(camera.position);
|
|
|
+ scene.add(mesh);
|
|
|
+ }
|
|
|
+
|
|
|
+ function tweenCameraAnmaChange(value) {
|
|
|
+ tweenCameraAnma = value
|
|
|
+ }
|
|
|
+
|
|
|
+ function attendEvent() {
|
|
|
+ renderer.domElement.addEventListener('touchstart', onPointerStart, false);
|
|
|
+ renderer.domElement.addEventListener('touchmove', onPointerMove, false);
|
|
|
+ renderer.domElement.addEventListener('touchend', onPointerUp, false);
|
|
|
+ }
|
|
|
+ //取消事件监听-避免二次进入时触发多次事件
|
|
|
+ function clearEvent() {
|
|
|
+ console.warn("**clearEvent****")
|
|
|
+ renderer && renderer.domElement && renderer.domElement.removeEventListener('touchstart', onPointerStart);
|
|
|
+ renderer && renderer.domElement && renderer.domElement.removeEventListener('touchmove', onPointerMove);
|
|
|
+ renderer && renderer.domElement && renderer.domElement.removeEventListener('touchend', onPointerUp);
|
|
|
+ }
|
|
|
+ // 手指移动开始
|
|
|
+ function onPointerStart(event) {
|
|
|
+ startTime = (new Date()).getTime();
|
|
|
+ fingerCount = event.touches.length; //手指个数
|
|
|
+ console.log('开始触摸事件:', lon, fingerCount, camera.position.y)
|
|
|
+ if (fingerCount === 1) {
|
|
|
+ // 只有一个手指时记录当前点的坐标作为平移起始点
|
|
|
+ onPointerDownMouseX = event.changedTouches[0].clientX;
|
|
|
+ onPointerDownMouseY = event.changedTouches[0].clientY;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ //持续触摸中
|
|
|
+ function onPointerMove(event) {
|
|
|
+ fingerCount = event.touches.length; //手指个数
|
|
|
+ }
|
|
|
+ //触摸结束
|
|
|
+ function onPointerUp(event) {
|
|
|
+ fingerCount = event.touches.length; //手指个数
|
|
|
+ console.warn("***触摸结束***", fingerCount, startTime)
|
|
|
+ if (fingerCount == 0) {
|
|
|
+ let now = new Date().getTime()
|
|
|
+ if (Math.abs(event.changedTouches[0].clientX - onPointerDownMouseX) < 10 &&
|
|
|
+ Math.abs(event.changedTouches[0].clientY - onPointerDownMouseY) < 10 &&
|
|
|
+ (now - startTime) < 300) {
|
|
|
+ checkIntersection(event);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //射线检测handle
|
|
|
+ function checkIntersection(event) {
|
|
|
+ let x = (event.changedTouches[0].clientX / screenWidth) * 2 - 1;
|
|
|
+ let y = -(event.changedTouches[0].clientY / that.canvasHeight) * 2 + 1;
|
|
|
+ mouse.x = x;
|
|
|
+ mouse.y = y;
|
|
|
+ //更新射线
|
|
|
+ raycaster.setFromCamera(mouse, camera);
|
|
|
+ let intersects = raycaster.intersectObjects(scene.children, true);
|
|
|
+ console.warn("***checkIntersection***", intersects.length)
|
|
|
+ if (intersects.length > 0) {
|
|
|
+ //找到最近的那个网格模型物体
|
|
|
+ let mesh = intersects.find((it) => {
|
|
|
+ if (it.object && it.object.isMesh == true &&
|
|
|
+ it.object.parent && it.object.parent.name == 'actor' &&
|
|
|
+ it.object.parent.visible == true) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ //拾取到了视角,就不继续拾取了
|
|
|
+ if (mesh) {
|
|
|
+ moveActor(mesh.object.parent);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ mesh = intersects.find((it) => {
|
|
|
+ if (it.object && it.object.isInstancedMesh &&
|
|
|
+ (it.object.name == '地板' || it.object.name == '花园') && it.object.visible == true) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ //拾取到了地板
|
|
|
+ if (mesh) {
|
|
|
+ let floor = mesh.object;
|
|
|
+ let index = mesh.instanceId; //射线相交是的实例序号
|
|
|
+ let spaceId = that.gltfSpaces[index].spaceId; //获取选中实例的空间id
|
|
|
+ if (floor.name == "花园") { //花园
|
|
|
+ return false;
|
|
|
+ let selectMesh = that.gltfSpaces.find(it => {
|
|
|
+ return it.spaceType == 14 && it.instancedMeshIndexList[0].instancedAtIndex == index
|
|
|
+ })
|
|
|
+ spaceId = selectMesh.spaceId;
|
|
|
+ } else { //室内
|
|
|
+ // floor.name = "地板";
|
|
|
+ let selectMesh = that.gltfSpaces.find(it => {
|
|
|
+ return it.spaceType != 14 && it.instancedMeshIndexList[0].instancedAtIndex == index
|
|
|
+ })
|
|
|
+ spaceId = selectMesh.spaceId;
|
|
|
+ }
|
|
|
+ // let spaceId = that.gltfSpaces[index].spaceId;//获取选中实例的空间id
|
|
|
+ console.warn("***checkIntersection-地板***", floor, index, spaceId, that.spaceId)
|
|
|
+ //当前拾取到的是本空间的底部-意味着用户点击了地板
|
|
|
+ if (floor && spaceId == that.spaceId) {
|
|
|
+ moveCarmer(mesh.point);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function tweenCamera(oldP, oldT, newP, newT, oldUp, newUp, time = 1000) {
|
|
|
+ if (JSON.stringify(oldP) == JSON.stringify(newP) && JSON.stringify(oldT) == JSON.stringify(newT)) {
|
|
|
+ that.repeatFlag = false; //放开限制,可以再次点击
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!chooseMesh) {
|
|
|
+ that.repeatFlag = false; //放开限制,可以再次点击
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ tweenCameraAnma = true;
|
|
|
+ var tween = new TWEEN.Tween({
|
|
|
+ x1: oldP.x, // 相机x
|
|
|
+ y1: oldP.y, // 相机y
|
|
|
+ z1: oldP.z, // 相机z
|
|
|
+ x2: oldT.x, // 控制点的中心点x
|
|
|
+ y2: oldT.y, // 控制点的中心点y
|
|
|
+ z2: oldT.z, // 控制点的中心点z
|
|
|
+ x3: oldUp.x, // 控制点的中心点x
|
|
|
+ y3: oldUp.y, // 控制点的中心点y
|
|
|
+ z3: oldUp.z // 控制点的中心点z
|
|
|
+ })
|
|
|
+ .to({
|
|
|
+ x1: newP.x,
|
|
|
+ y1: newP.y,
|
|
|
+ z1: newP.z,
|
|
|
+ x2: newT.x,
|
|
|
+ y2: newT.y,
|
|
|
+ z2: newT.z,
|
|
|
+ x3: newUp.x, // up向量
|
|
|
+ y3: newUp.y, // 控制点的中心点y
|
|
|
+ z3: newUp.z // 控制点的中心点z
|
|
|
+ }, time)
|
|
|
+ .easing(TWEEN.Easing.Quadratic.InOut)
|
|
|
+ .onUpdate((object) => {
|
|
|
+ camera.position.x = object.x1;
|
|
|
+ camera.position.y = object.y1;
|
|
|
+ camera.position.z = object.z1;
|
|
|
+ // let newTarget = new THREE.Vector3(object.x3,object.y3,object.z3);
|
|
|
+ // camera.up.copy(newTarget);
|
|
|
+ camera.lookAt(object.x2, object.y2, object.z2);
|
|
|
+ // controls.target.x = object.x2;
|
|
|
+ // controls.target.y = object.y2;
|
|
|
+ // controls.target.z = object.z2;
|
|
|
+ // controls.update();
|
|
|
+ }).onComplete(() => {
|
|
|
+ controls.target.x = newT.x;
|
|
|
+ controls.target.y = newT.y;
|
|
|
+ controls.target.z = newT.z;
|
|
|
+ //修正最后的视角
|
|
|
+ // let up = new THREE.Vector3(newUp.x,newUp.y,newUp.z);
|
|
|
+ // camera.up.copy(up);
|
|
|
+ camera.lookAt(newT.x, newT.y, newT.z);
|
|
|
+ tweenCameraAnma = false;
|
|
|
+ that.repeatFlag = false; //放开限制,可以再次点击
|
|
|
+ })
|
|
|
+ // 开始动画
|
|
|
+ tween.start();
|
|
|
+ }
|
|
|
+
|
|
|
+ function stopRender() {
|
|
|
+ needRender = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ function starRender() {
|
|
|
+ if (needRender == true) { //如果已经在渲染中了,则不能再次开启,避免渲染过多
|
|
|
+ false;
|
|
|
+ }
|
|
|
+ needRender = true;
|
|
|
+ render(); //开始渲染
|
|
|
+ }
|
|
|
+
|
|
|
+ function render() {
|
|
|
+ if (needRender == false) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ TWEEN && TWEEN.update();
|
|
|
+ // stats.update();
|
|
|
+ renderer.render(scene, camera); //单次渲染
|
|
|
+ requestId = requestAnimationFrame(render, canvas3d);
|
|
|
+ if (that.screenshotResolve) {
|
|
|
+ stopRender();
|
|
|
+ that.screenshotResolve()
|
|
|
+ that.screenshotResolve = null; //释放Promise
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+ // computed: {
|
|
|
+ // curHouseObj() {
|
|
|
+ // return this.$store.state.curHouseObj;
|
|
|
+ // },
|
|
|
+ // wallList() {
|
|
|
+ // return this.$store.state.wallList;
|
|
|
+ // },
|
|
|
+ // },
|
|
|
+ methods: {
|
|
|
+ navbarBackClk() {
|
|
|
+
|
|
|
+ },
|
|
|
+ clearHandle() {
|
|
|
+ this.clearEvent();
|
|
|
+ },
|
|
|
+ save() {
|
|
|
+ this.$refs.viewMask.save(); //下载
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 设置户型详情信息
|
|
|
+ * @param {Object} data 户型详情
|
|
|
+ */
|
|
|
+ setHouseDetail(data) {
|
|
|
+ if (data) {
|
|
|
+ this.id = data.id;
|
|
|
+ this.spaceId = this.$route.query.spaceId ? this.$route.query.spaceId : '';
|
|
|
+ console.warn("***curHouseObj***", data)
|
|
|
+ // 加载户型
|
|
|
+ this.loadSpace();
|
|
|
+ } else {
|
|
|
+ this.curHouseObj = {}
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 绘制空间-即地板
|
|
|
+ async loadSpace() {
|
|
|
+ this.spaceList = [];
|
|
|
+ this.wallIds = [];
|
|
|
+ if (!this.curHouseObj || !this.spaceId) { //减少重复请求
|
|
|
+ alert("数据错误")
|
|
|
+ console.warn("***数据错误***")
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if (this.curHouseObj) {
|
|
|
+ const spaceDetail = this.curHouseObj;
|
|
|
+ const spaceList = JSON.parse(spaceDetail.houseJson);
|
|
|
+ // 交换centerX, centerY;上一页面已经处理过了,这里不在需要处理
|
|
|
+ for (let index = 0; index < spaceList.length; index++) {
|
|
|
+ var element = spaceList[index];
|
|
|
+ // const centerX = JSON.parse(JSON.stringify(element.centerX))
|
|
|
+ if (!element.actors || element.actors.length == 0) {
|
|
|
+ element.actors = [{
|
|
|
+ actorLocation: element.actorLocation,
|
|
|
+ actorTransform: element.actorTransform,
|
|
|
+ isSelected: true,
|
|
|
+ }]
|
|
|
+ }
|
|
|
+ element.actors.forEach(actor => {
|
|
|
+ let _actorLoaction = actor.actorLocation.split(','); //x y z
|
|
|
+ let X_C = parseInt(_actorLoaction[0]); //X轴偏移量-UE原因
|
|
|
+ let Y_C = -parseInt(_actorLoaction[1]); //Y轴偏移量-取反,UE里面的Y轴方向跟Three.js相反
|
|
|
+ let _x = element.centerX + X_C;
|
|
|
+ let _z = -element.centerY + Y_C; //centerY 要取反,因为UE里面是反向的
|
|
|
+
|
|
|
+ // let _x = parseInt(_actorLoaction[1]) || element.centerX;//观察点 X轴坐标
|
|
|
+ // let _z = parseInt(_actorLoaction[0]) || element.centerY;//观察点 Z轴坐标
|
|
|
+ let _presentX = (_x - element.centerX) / ((element.spaceWidth / 2) -
|
|
|
+ 10); //10是墙壁厚度-单位cm
|
|
|
+ let _presentY = (_z + element.centerY) / ((element.spaceHeight / 2) - 10);
|
|
|
+ //注意如果一开始就设置大超过空间大小,则处理成贴近空间边界
|
|
|
+ actor.presentX = Math.abs(_presentX) > 1 ? (_presentX > 1 ? 1 : -1) :
|
|
|
+ _presentX; //观察点跟空间中心原点的距离比例
|
|
|
+ actor.presentY = Math.abs(_presentY) > 1 ? (_presentY > 1 ? 1 : -1) : _presentY;
|
|
|
+ })
|
|
|
+ element.wallMoveValue = "[0,0,0,0]"
|
|
|
+ this.spaceList.push(element);
|
|
|
+ this.wallIds.push(element.wallId);
|
|
|
+
|
|
|
+ if (element.spaceId == this.spaceId) { // 默认选中空间
|
|
|
+ this.curSpaceObj = element;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!this.curSpaceObj && this.spaceList.length > 0) {
|
|
|
+ this.curSpaceObj = this.spaceList[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let curSpaceArea = parseFloat((this.curSpaceObj.spaceWidth * this.curSpaceObj.spaceHeight) / 10000)
|
|
|
+ .toFixed(1);
|
|
|
+ this.navbar.title = this.curSpaceObj.spaceName + " " + curSpaceArea + "㎡"
|
|
|
+ console.log("该户型空间数据:", this.spaceList);
|
|
|
+ console.log("当前选中的空间:", this.curSpaceObj);
|
|
|
+ // 获取墙体数据并且绘制墙体
|
|
|
+ },
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+ @import "./webgl_rxdz_vr.scss";
|
|
|
+ /* @import "@/common/css/common.css"; */
|
|
|
+</style>
|