// var app = getApp(); //获取应用实例 // const util = require('@/static/utils/util.js'); // const config = require('@/services/urlConfig.js'); // import requestConfig from '@/services/requestConfig.js'; import * as THREE from 'three'; import TWEEN from 'three/addons/libs/tween.module.js'; let glbWidth = 300;//空间地板模型的真实尺寸 let glbHeight = 300; export default { data() { return { gltfSpaceUrl: "https://dm.static.elab-plus.com/miniProgram/model/BP_AutoWall_WoodFloor1.glb", //模型地址 // gltfSpaceUrl: "https://dm.static.elab-plus.com/3d/model/20230607/diban/diban.gltf", //模型地址 // gltfSpaceUrl: "https://dm.static.elab-plus.com/miniProgram/model/BP_L_carpet01-1.gltf", //模型地址 // gltfSpace1Url: "https://dm.static.elab-plus.com/miniProgram/model/BP_G_tiles01_1.gltf", //模型地址 gltfSpace1Url: "https://dm.static.elab-plus.com/miniProgram/model/BP_G_tiles01.glb", //模型地址 instancedSpaceMeshList:[],//地板实例对象 当地板复杂是就会存在多个 spaceTypes : ["卧室","客厅","餐厅","厨房","玄关","卫生间","衣帽间","收纳","阳台","飘窗","链接空间","自定义","楼梯","花园"], } }, watch: {}, methods: { // 加载地板模型 loaderSpaceArr(list){ if(!list || list.length==0){ return false; } this.instancedSpaceMeshList = []; this.gltfSpaces = []; let comlist = list.filter(it=>it.spaceType!=14);//过滤花园的空间 this.loaderCommonSpace(this.gltfSpaceUrl,comlist,1); let arrlist = list.filter(it=>it.spaceType==14);//花园的空间 this.loaderCommonSpace(this.gltfSpace1Url,arrlist,2); }, loaderCommonSpace(gltfSpaceUrl,list,type=1){ var that = this; this.loader.load(gltfSpaceUrl, ( gltf ) => { console.log("地板模型加载成功",list) // gltf.scene.receiveShadow = true;//材质是否接收阴影 gltf.scene.traverse((child)=> { if (child.isMesh && child.visible) { let instancedMesh = new THREE.InstancedMesh(child.geometry.clone(), child.material.clone(), list.length); this.instancedSpaceMeshList.push(instancedMesh); //设置每一块地板的实例值 list.forEach((obj,i)=>{ let positionX = obj.centerX / 100; let positionY = obj.centerY / 100; if(type==1){ // glbWidth = glbHeight = 1500; } let scaleX = obj.spaceWidth / glbWidth; let scaleY = obj.spaceHeight / glbHeight; gltf.scene.position.set(positionX, 0, -positionY); gltf.scene.scale.set(scaleX, 1, scaleY); gltf.scene.updateMatrixWorld();//更新世界坐标-这样,子模型也同步更新了 instancedMesh.setMatrixAt(i, child.matrixWorld); instancedMesh.instanceMatrix.needsUpdate = true; instancedMesh.setColorAt(i, child.material.color); instancedMesh.instanceColor.needsUpdate = true;//打开颜色修改开关,不开,颜色是不能修改额 let gltfSpace = that.gltfSpaces.find(gltfSpace=>gltfSpace.spaceId == obj.spaceId);//判断是否已经添加过 if(!gltfSpace){ let position = new THREE.Vector3();//当前几何体的位置参数 let scale = new THREE.Vector3();//当前几何体的缩放参数 position.set(positionX, 0, -positionY); scale.set(scaleX, 1, scaleY); let md = { spaceId:obj.spaceId,//模型实例的唯一标识 instancedMeshIndexList:[//标识网格实例数组的序号 以及 当前几何体 在网格实例的序号 {instancedMeshIndex: this.instancedSpaceMeshList.length - 1, instancedAtIndex : i}, ], spaceName:obj.spaceName,//几何体的id spaceType:obj.spaceType, position:position, scale:scale, isSizeLock:obj.isSizeLock, }; that.gltfSpaces.push(md); }else{//添加另外一组实例 gltfSpace.instancedMeshIndexList.push({ instancedMeshIndex:this.instancedSpaceMeshList.length - 1, instancedAtIndex:i }) } }) instancedMesh.userType = "mesh"; if(type==2){//花园 instancedMesh.name = "花园"; }else{//室内 instancedMesh.name = "地板"; } instancedMesh.receiveShadow = true;//材质是否接收阴影 that.scene.add(instancedMesh); } }); // this.pvCurPageName=="room_show" if(true){ //给地板模型添加天花板 list && list.forEach(obj=>{ if(obj.spaceType!=14){//花园不加上面的墙 let positionX = obj.centerX / 100; let positionY = obj.centerY / 100; // 天花板 const planeGeometry = new THREE.PlaneGeometry(obj.spaceWidth / 100,obj.spaceHeight / 100); const planeMaterial = new THREE.MeshStandardMaterial({ color:0xffffff, metalness: 0.4, // 设置金属度为1.0 roughness: 1 // 设置粗糙度为0.5 }); const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial) planeMesh.rotation.x = Math.PI / 2 ; // 旋转 180 度 planeMesh.position.set(positionX, 2.8, -positionY); planeMesh.userType = "mesh"; that.scene.add(planeMesh); } }) } }); }, //修改当前空间的面积大小 changeSpacesAnimOld(curSpace){ // 寻找地板 const cube = this.gltfSpaces.find((item)=>{ return item.spaceId == curSpace.spaceId; }) console.log("空间移动目标",cube.spaceId, JSON.stringify(cube.position),JSON.stringify(cube.scale),curSpace.toScaleX, curSpace.toScaleZ, curSpace.toPx, curSpace.toPz); //获取地板模型的geometry实例 cube.instancedMeshIndexList.forEach(instanced=>{ let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局InstancedMesh实例的位置 let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例 let stratMatrix = new THREE.Matrix4();//定义一个四维矩阵 instancedMesh.getMatrixAt(instanced.instancedAtIndex,stratMatrix);//获取当前几何体的四维矩阵到stratMatrix里面 let endMatrix = stratMatrix.clone();//复制一个四维矩阵 let scaleMatrix = new THREE.Matrix4();//定义一个缩放变化矩阵 let panMatrix = new THREE.Matrix4();//定义一个平移变化矩阵 scaleMatrix.makeScale(curSpace.toScaleX / cube.scale.x,1,curSpace.toScaleZ / cube.scale.z); //获得缩放变化矩阵 panMatrix.makeTranslation(curSpace.toPx - cube.position.x,0,curSpace.toPz - cube.position.z); //获得平移变化矩阵 endMatrix.multiply(scaleMatrix).premultiply(panMatrix);//通过矩阵计算获得最终的矩阵 // console.warn("***drawModel-isAnimate-space***",JSON.stringify(endMatrix1),JSON.stringify(endMatrix),JSON.stringify(stratMatrix)); var tween = new TWEEN.Tween(stratMatrix.elements) .to(endMatrix.elements, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .onUpdate((matrixWorld)=> { let m4 = new THREE.Matrix4();//定义一个四维矩阵 m4.set(...matrixWorld);//注意:四维矩阵的显示和实际计算的行列优先规则不同 instancedMesh.instanceMatrix.needsUpdate = true;//更新之前,必须开启开关 instancedMesh.setMatrixAt(instanced.instancedAtIndex,m4.transpose());//更新几何体的世界矩阵 }).onComplete(()=>{ instancedMesh.setMatrixAt(instanced.instancedAtIndex,endMatrix);//更新几何体的世界矩阵 this.tweenCameraAnmaChange(false) }); // 开始动画 tween.start(); this.tweenCameraAnmaChange(true) cube.position.x = curSpace.toPx; cube.position.z = curSpace.toPz; cube.scale.x = curSpace.toScaleX; cube.scale.z = curSpace.toScaleZ; }); }, //修改当前空间的面积大小-动画方案2 changeSpacesAnim(curSpace){ // 寻找地板 const cube = this.gltfSpaces.find((item)=>{ return item.spaceId == curSpace.spaceId; }) console.log("空间移动目标",cube.spaceId, JSON.stringify(cube.position),JSON.stringify(cube.scale), curSpace.toPx, curSpace.toPz,curSpace.toScaleX, curSpace.toScaleZ); //初始形变值 let x = cube.position.x; let z = cube.position.z; let scaleX = cube.scale.x; let scaleZ = cube.scale.z; //最终形变值 let toPx = curSpace.toPx; let toPz = curSpace.toPz; let toScaleX = curSpace.toScaleX; let toScaleZ = curSpace.toScaleZ; let spaceInitMatrix = [];//空间动画时的初始变换矩阵 cube.instancedMeshIndexList.forEach(instanced=>{ let _index = instanced.instancedMeshIndex;//geometry实例 在 全局InstancedMesh实例的位置 let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例 let startMatrix = new THREE.Matrix4();//定义一个四维矩阵 instancedMesh.getMatrixAt(instanced.instancedAtIndex,startMatrix);//获取当前几何体的四维矩阵到stratMatrix里面 spaceInitMatrix.push({ index:_index, matrix:startMatrix.clone(), color:instancedMesh.material.color.clone(), }) }); // console.warn("***cube***",cube.spaceId,Date.now(),JSON.stringify(spaceInitMatrix[0].matrix)) var tween = new TWEEN.Tween({ x: cube.position.x, z: cube.position.z, sx:cube.scale.x, sz:cube.scale.z }) .to({ x: curSpace.toPx, z: curSpace.toPz, sx:curSpace.toScaleX, sz:curSpace.toScaleZ }, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .onUpdate((object)=> { //获取地板模型的geometry实例 cube.instancedMeshIndexList.forEach(instanced=>{ let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局InstancedMesh实例的位置 let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例 //获取对象实例的初始变换矩阵 let tmp = spaceInitMatrix.find(it=>it.index==_index); let stratMatrix = tmp.matrix.clone(); //获取初始变换矩阵 let scaleMatrix = new THREE.Matrix4(); //定义一个缩放变化矩阵 let panMatrix = new THREE.Matrix4(); //定义一个平移变化矩阵 scaleMatrix.makeScale(object.sx / scaleX,1,object.sz / scaleZ); //获得缩放变化矩阵 panMatrix.makeTranslation(object.x - x,0,object.z - z); //获得平移变化矩阵 stratMatrix.multiply(scaleMatrix).premultiply(panMatrix);//通过矩阵计算获得最终的形变矩阵 instancedMesh.instanceMatrix.needsUpdate = true;//更新之前,必须开启开关 instancedMesh.setMatrixAt(instanced.instancedAtIndex,stratMatrix);//更新几何体的世界矩阵 if(this.curSpaceObj.spaceId==cube.spaceId){//当前选中的空间才变化颜色 instancedMesh.instanceColor.needsUpdate = true;//打开颜色修改开关,不开,颜色是不能修改额 let color = new THREE.Color(0xFF9F40); // 使用sRGB颜色值 // color.convertSRGBToLinear(); // 将颜色值转换为线性颜色值 instancedMesh.setColorAt(instanced.instancedAtIndex, color);//修改这个几何体的颜色 } }); }).onComplete(()=>{//这个回调很可能会很慢 this.tweenCameraAnmaChange(false); console.warn("***changeSpacesAnim-over***") if(this.curSpaceObj.spaceId==cube.spaceId){//当前选中的空间才恢复颜色 cube.instancedMeshIndexList.forEach(instanced=>{ let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局Instance let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例 let tmp = spaceInitMatrix.find(it=>it.index==_index); instancedMesh.instanceColor.needsUpdate = true;//打开颜色修改开关,不开,颜色是不能修改额 instancedMesh.setColorAt(instanced.instancedAtIndex, tmp.color);//修改这个几何体的颜色 }); } }); // 开始动画 tween.start(); this.tweenCameraAnmaChange(true) cube.position.x = curSpace.toPx; cube.position.z = curSpace.toPz; cube.scale.x = curSpace.toScaleX; cube.scale.z = curSpace.toScaleZ; }, //所有空间整体缩放-同时同步到数据里面 allSpaceScale(){ const centerOffset = new THREE.Vector3(0, 0, 0); // 假设中心点在几何体的正中心 const scale = new THREE.Vector3(0.9, 1, 0.9); // 缩放尺度 //实例化的默认中心是就原点也就是0 0 0 这个点,所以centerOffset也必须是原点 // this.instancedMeshList.forEach((mesh)=>{ // mesh.scale.copy(scale); // mesh.updateMatrix(); // mesh.updateMatrixWorld(); // mesh.updateMorphTargets(); // mesh.geometry.computeVertexNormals(); // }) // this.instancedSpaceMeshList.forEach((mesh)=>{ // mesh.scale.copy(scale); // mesh.updateMatrix(); // mesh.updateMatrixWorld(); // mesh.updateMorphTargets(); // mesh.geometry.computeVertexNormals(); // }) // this.gltfLayouts.forEach((mesh)=>{ // // mesh.scale.copy(scale); // }) this.gltfSpaces.forEach(cube=>{ let dis = centerOffset.clone().sub(cube.position);//获得差值向量 // console.warn("***dis***",JSON.stringify(dis),JSON.stringify(cube.position),cube.spaceId); let x = dis.x * (1 - scale.x);//x轴需要移动的距离 let y = dis.y * (1 - scale.y);//y轴需要移动的距离 let z = dis.z * (1 - scale.z);//z轴需要移动的距离 let pi = new THREE.Vector3();//新的中心点位置 pi.x = cube.position.x + x; pi.y = cube.position.y + y; pi.z = cube.position.z + z; cube.scale.x = cube.scale.x * scale.x;//修改真实的比例 cube.scale.z = cube.scale.z * scale.z;// cube.position.copy(pi); //同步信息到空间列表中 let sapce = this.spaceList.find(it=>{ return it.spaceId==cube.spaceId}); if(sapce){ //更新空间中心点值 sapce.centerX = pi.x * 100; sapce.centerY = -1* pi.z * 100; //更新空间宽度高度值 sapce.spaceWidth = cube.scale.x * glbWidth; sapce.spaceHeight = cube.scale.z * glbHeight; } // let dir = new THREE.Vector3( 0, 10, 0 );;//当前几何体的位置参数 // console.warn("***dis1***",JSON.stringify(pi),JSON.stringify(dir)); // let length = 3; // let hex = 0xff0000; // let arrowHelper = new THREE.ArrowHelper( dir, pi, length, hex ); // this.scene.add( arrowHelper ); //这是矩阵运算处理缩放的方法-此为数学上的方案 cube.instancedMeshIndexList.forEach(instanced=>{ let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局InstancedMesh实例的位置 let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例 let stratMatrix = new THREE.Matrix4();//定义一个四维矩阵 instancedMesh.getMatrixAt(instanced.instancedAtIndex,stratMatrix);//获取当前几何体的四维矩阵到stratMatrix里面 let scaleMatrix = new THREE.Matrix4().makeScale(scale.x, scale.y, scale.z); let inverseTranslationMatrix = new THREE.Matrix4().makeTranslation(x, y, z); stratMatrix.premultiply(inverseTranslationMatrix); stratMatrix.multiply(scaleMatrix); instancedMesh.instanceMatrix.needsUpdate = true; instancedMesh.setMatrixAt(instanced.instancedAtIndex,stratMatrix);//更新几何体的世界矩阵 }); }) this.$nextTick(()=>{ this.updateAllWallHandle(); }) setTimeout(() =>{ this.updataPageData();//更新数据到各个对象里面 this.calculateLayoutModelSize();//重新计算家具位置 this.updateLables();//更新lable this.$nextTick(()=>{ this.updateCareFul();//更新精细调整里面的空间 this.updateSpanceData();//更新语音组件里面的空间 }) }, 100); } } }