floorMethod.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. // var app = getApp(); //获取应用实例
  2. // const util = require('@/static/utils/util.js');
  3. // const config = require('@/services/urlConfig.js');
  4. // import requestConfig from '@/services/requestConfig.js';
  5. import * as THREE from 'three';
  6. import TWEEN from 'three/addons/libs/tween.module.js';
  7. import spaceTypes from '@/static/spaceTypesIE.js';
  8. let glbWidth = 300;//空间地板模型的真实尺寸
  9. let glbHeight = 300;
  10. export default {
  11. data() {
  12. return {
  13. gltfSpaceUrl: "https://dm.static.elab-plus.com/miniProgram/model/BP_AutoWall_WoodFloor1.glb", //模型地址
  14. // gltfSpaceUrl: "https://dm.static.elab-plus.com/3d/model/20230607/diban/diban.gltf", //模型地址
  15. // gltfSpaceUrl: "https://dm.static.elab-plus.com/miniProgram/model/BP_L_carpet01-1.gltf", //模型地址
  16. // gltfSpace1Url: "https://dm.static.elab-plus.com/miniProgram/model/BP_G_tiles01_1.gltf", //模型地址
  17. gltfSpace1Url: "https://dm.static.elab-plus.com/miniProgram/model/BP_G_tiles01.glb", //模型地址
  18. spaceTypes:spaceTypes,
  19. }
  20. },
  21. watch: {},
  22. methods: {
  23. // 加载地板模型
  24. loaderSpaceArr(list){
  25. if(!list || list.length==0){
  26. return false;
  27. }
  28. this.instancedSpaceMeshList = [];
  29. this.gltfSpaces = [];
  30. let comlist = list.filter(it=>it.spaceType!=14);//过滤花园的空间
  31. this.loaderCommonSpace(this.gltfSpaceUrl,comlist,1);
  32. let arrlist = list.filter(it=>it.spaceType==14);//花园的空间
  33. // this.loaderCommonSpace(this.gltfSpace1Url,arrlist,2);
  34. if(arrlist && arrlist.length>0){
  35. this.loaderGraseSpace(list);//加载草地的模型-所有地块都添加,填补房间里面的
  36. }
  37. },
  38. loaderCommonSpace(gltfSpaceUrl,list,type=1){
  39. var that = this;
  40. this.loader.load(gltfSpaceUrl, ( gltf ) => {
  41. console.log("地板模型加载成功")
  42. // gltf.scene.receiveShadow = true;//材质是否接收阴影
  43. gltf.scene.traverse((child)=> {
  44. if (child.isMesh && child.visible) {
  45. let instancedMesh = new THREE.InstancedMesh(child.geometry.clone(), child.material.clone(), list.length);
  46. this.instancedSpaceMeshList.push(instancedMesh);
  47. //设置每一块地板的实例值
  48. list.forEach((obj,i)=>{
  49. let positionX = obj.centerX / 100;
  50. let positionY = obj.centerY / 100;
  51. if(type==1){
  52. // glbWidth = glbHeight = 1500;
  53. }
  54. let scaleX = obj.spaceWidth / glbWidth;
  55. let scaleY = obj.spaceHeight / glbHeight;
  56. gltf.scene.position.set(positionX, 0, -positionY);
  57. gltf.scene.scale.set(scaleX, 1, scaleY);
  58. gltf.scene.updateMatrixWorld();//更新世界坐标-这样,子模型也同步更新了
  59. instancedMesh.setMatrixAt(i, child.matrixWorld);
  60. instancedMesh.instanceMatrix.needsUpdate = true;
  61. instancedMesh.setColorAt(i, child.material.color);
  62. instancedMesh.instanceColor.needsUpdate = true;//打开颜色修改开关,不开,颜色是不能修改额
  63. let gltfSpace = that.gltfSpaces.find(gltfSpace=>gltfSpace.spaceId == obj.spaceId);//判断是否已经添加过
  64. if(!gltfSpace){
  65. let position = new THREE.Vector3();//当前几何体的位置参数
  66. let scale = new THREE.Vector3();//当前几何体的缩放参数
  67. position.set(positionX, 0, -positionY);
  68. scale.set(scaleX, 1, scaleY);
  69. let md = {
  70. spaceId:obj.spaceId,//模型实例的唯一标识
  71. instancedMeshIndexList:[//标识网格实例数组的序号 以及 当前几何体 在网格实例的序号
  72. {instancedMeshIndex: this.instancedSpaceMeshList.length - 1, instancedAtIndex : i,
  73. color:instancedMesh.material.color.clone(),},
  74. ],
  75. spaceName:obj.spaceName,//几何体的id
  76. spaceType:obj.spaceType,
  77. position:position,
  78. scale:scale,
  79. isSizeLock:obj.isSizeLock,
  80. };
  81. that.gltfSpaces.push(md);
  82. }else{//添加另外一组实例
  83. gltfSpace.instancedMeshIndexList.push({
  84. instancedMeshIndex:this.instancedSpaceMeshList.length - 1, instancedAtIndex:i,
  85. color:instancedMesh.material.color.clone(),
  86. })
  87. }
  88. })
  89. instancedMesh.userType = "mesh";
  90. if(type==2){//花园
  91. instancedMesh.name = "花园";
  92. }else{//室内
  93. instancedMesh.name = "地板";
  94. }
  95. instancedMesh.receiveShadow = true;//材质是否接收阴影
  96. that.scene.add(instancedMesh);
  97. }
  98. });
  99. // this.pvCurPageName=="room_show"
  100. if(true){
  101. //给地板模型添加天花板
  102. list && list.forEach(obj=>{
  103. if(obj.spaceType!=14){//花园不加上面的墙
  104. let positionX = obj.centerX / 100;
  105. let positionY = obj.centerY / 100;
  106. // 天花板
  107. const planeGeometry = new THREE.PlaneGeometry(obj.spaceWidth / 100,obj.spaceHeight / 100);
  108. const planeMaterial = new THREE.MeshStandardMaterial({
  109. color:0xffffff,
  110. metalness: 0.4, // 设置金属度为1.0
  111. roughness: 1 // 设置粗糙度为0.5
  112. });
  113. const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
  114. planeMesh.rotation.x = Math.PI / 2 ; // 旋转 180 度
  115. planeMesh.position.set(positionX, 2.8, -positionY);
  116. planeMesh.userType = "mesh"
  117. const spaceObj = {
  118. spaceId:obj.spaceId,
  119. spaceWidth:obj.spaceWidth,
  120. spaceHeight:obj.spaceHeight
  121. }
  122. planeMesh.userData = spaceObj;
  123. that.scene.add(planeMesh);
  124. that.gltfSpaceRoofs.push(planeMesh);
  125. }
  126. })
  127. }
  128. });
  129. },
  130. //草地加载
  131. loaderGraseSpace(list){
  132. let glbWidth = 300;
  133. let glbHeight = 300;
  134. var that = this;
  135. console.log("草地模型加载成功")
  136. //将模型添加到场景中
  137. let texture = new THREE.TextureLoader().load("https://dm.static.elab-plus.com/miniProgram/grass.jpg");
  138. // let texture = new THREE.TextureLoader().load( "https://dm.static.elab-plus.com/miniProgram/Avatar_male.png" );
  139. texture.wrapS = THREE.RepeatWrapping;
  140. texture.wrapT = THREE.RepeatWrapping;
  141. list && list.forEach(obj=>{
  142. // 暂时手动过滤 2楼 儿童房和次卫 不添加草地
  143. if((obj.spaceId == 582 && obj.spaceName == "儿童房") || (obj.spaceId == 388 && obj.spaceName == "次卫")){
  144. return
  145. }
  146. let positionX = obj.centerX / 100;
  147. let positionY = obj.centerY / 100;
  148. let scaleX = obj.spaceWidth / 100;
  149. let scaleY = obj.spaceHeight / 100;
  150. texture = texture.clone();
  151. //使用纹理贴图材质
  152. texture.repeat.set(scaleX, scaleY);
  153. // let geometry = new THREE.PlaneGeometry(obj.spaceWidth/100, obj.spaceHeight/100);
  154. // let material = new THREE.MeshBasicMaterial({ map: texture });
  155. // let cube = new THREE.Mesh(geometry, material);
  156. let geometry = new THREE.BoxGeometry(obj.spaceWidth/100, 0.1, obj.spaceHeight/100);
  157. let colorMaterial = new THREE.MeshBasicMaterial({color: 0x626F44}); // 创建纯色材质对象
  158. let material = new THREE.MeshBasicMaterial({ map: texture });
  159. let cube = new THREE.Mesh(geometry, [colorMaterial, colorMaterial, material, colorMaterial, colorMaterial, colorMaterial]); // 使用 BoxGeometry 和纯色材质创建 Mesh 对象
  160. cube.name = "地板";
  161. cube.userType = "mesh";
  162. cube.userData = obj;//位置数据
  163. // 设置位置,旋转,缩放
  164. cube.position.set(positionX, -0.11, -positionY);
  165. // cube.rotation.x = -Math.PI / 2 ; // 旋转 180 度 PlaneGeometry 生效
  166. // cube.receiveShadow = true;//材质是否接收阴影
  167. //注释掉,不让草地进入空间列表中,不触发空间变化的动画过程,规避掉动画过程中的问题
  168. // let md = {
  169. // spaceId:obj.spaceId,//模型实例的唯一标识
  170. // spaceName:obj.spaceName,//几何体的id
  171. // spaceType:obj.spaceType,
  172. // position:cube.position,
  173. // scale:cube.scale,
  174. // isSizeLock:obj.isSizeLock,
  175. // };
  176. // that.gltfSpaces.push(md);
  177. that.scene.add(cube);
  178. })
  179. },
  180. //修改指定空间的颜色
  181. changeSpaceColor(spaceId,type=1){
  182. // 寻找地板
  183. const cube = this.gltfSpaces.find((item)=>{
  184. return item.spaceId == spaceId;
  185. })
  186. if(cube.instancedMeshIndexList){
  187. if(type==1){
  188. cube.instancedMeshIndexList.forEach(instanced=>{
  189. let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局InstancedMesh实例的位置
  190. let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例
  191. instancedMesh.instanceColor.needsUpdate = true;//打开颜色修改开关,不开,颜色是不能修改额
  192. let color = new THREE.Color(0xFF9F40); // 使用sRGB颜色值
  193. // color.convertSRGBToLinear(); // 将颜色值转换为线性颜色值
  194. instancedMesh.setColorAt(instanced.instancedAtIndex, color);//修改这个几何体的颜色
  195. })
  196. }else{
  197. cube.instancedMeshIndexList.forEach(instanced=>{
  198. let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局Instance
  199. let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例
  200. instancedMesh.instanceColor.needsUpdate = true;//打开颜色修改开关,不开,颜色是不能修改额
  201. instancedMesh.setColorAt(instanced.instancedAtIndex, instanced.color);//修改这个几何体的颜色
  202. });
  203. }
  204. }
  205. },
  206. //修改当前空间的面积大小-动画方案2
  207. changeSpacesAnim(curSpace){
  208. // 寻找地板
  209. const cube = this.gltfSpaces.find((item)=>{
  210. return item.spaceId == curSpace.spaceId;
  211. })
  212. console.log("空间移动目标",cube.spaceId, JSON.stringify(cube.position),JSON.stringify(cube.scale),
  213. curSpace.toPx, curSpace.toPz,curSpace.toScaleX, curSpace.toScaleZ);
  214. //初始形变值
  215. let x = cube.position.x;
  216. let z = cube.position.z;
  217. let scaleX = cube.scale.x;
  218. let scaleZ = cube.scale.z;
  219. //最终形变值
  220. let toPx = curSpace.toPx;
  221. let toPz = curSpace.toPz;
  222. let toScaleX = curSpace.toScaleX;
  223. let toScaleZ = curSpace.toScaleZ;
  224. if(cube.instancedMeshIndexList){
  225. let spaceInitMatrix = [];//空间动画时的初始变换矩阵
  226. cube.instancedMeshIndexList.forEach(instanced=>{
  227. let _index = instanced.instancedMeshIndex;//geometry实例 在 全局InstancedMesh实例的位置
  228. let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例
  229. let startMatrix = new THREE.Matrix4();//定义一个四维矩阵
  230. instancedMesh.getMatrixAt(instanced.instancedAtIndex,startMatrix);//获取当前几何体的四维矩阵到stratMatrix里面
  231. spaceInitMatrix.push({
  232. index:_index,
  233. matrix:startMatrix.clone(),
  234. color:instancedMesh.material.color.clone(),
  235. })
  236. });
  237. // console.warn("***cube***",cube.spaceId,Date.now(),JSON.stringify(spaceInitMatrix[0].matrix))
  238. var tween = new TWEEN.Tween({
  239. x: cube.position.x,
  240. z: cube.position.z,
  241. sx:cube.scale.x,
  242. sz:cube.scale.z
  243. })
  244. .to({
  245. x: curSpace.toPx,
  246. z: curSpace.toPz,
  247. sx:curSpace.toScaleX,
  248. sz:curSpace.toScaleZ
  249. }, 2000)
  250. .easing(TWEEN.Easing.Quadratic.InOut)
  251. .onUpdate((object)=> {
  252. //获取地板模型的geometry实例
  253. cube.instancedMeshIndexList.forEach(instanced=>{
  254. let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局InstancedMesh实例的位置
  255. let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例
  256. //获取对象实例的初始变换矩阵
  257. let tmp = spaceInitMatrix.find(it=>it.index==_index);
  258. let stratMatrix = tmp.matrix.clone(); //获取初始变换矩阵
  259. let scaleMatrix = new THREE.Matrix4(); //定义一个缩放变化矩阵
  260. let panMatrix = new THREE.Matrix4(); //定义一个平移变化矩阵
  261. scaleMatrix.makeScale(object.sx / scaleX,1,object.sz / scaleZ); //获得缩放变化矩阵
  262. panMatrix.makeTranslation(object.x - x,0,object.z - z); //获得平移变化矩阵
  263. stratMatrix.multiply(scaleMatrix).premultiply(panMatrix);//通过矩阵计算获得最终的形变矩阵
  264. instancedMesh.instanceMatrix.needsUpdate = true;//更新之前,必须开启开关
  265. instancedMesh.setMatrixAt(instanced.instancedAtIndex,stratMatrix);//更新几何体的世界矩阵
  266. if(this.curSpaceObj.spaceId==cube.spaceId){//当前选中的空间才变化颜色
  267. instancedMesh.instanceColor.needsUpdate = true;//打开颜色修改开关,不开,颜色是不能修改额
  268. let color = new THREE.Color(0xFF9F40); // 使用sRGB颜色值
  269. // color.convertSRGBToLinear(); // 将颜色值转换为线性颜色值
  270. instancedMesh.setColorAt(instanced.instancedAtIndex, color);//修改这个几何体的颜色
  271. }
  272. });
  273. }).onComplete(()=>{//这个回调很可能会很慢
  274. this.tweenCameraAnmaChange(false);
  275. console.warn("***changeSpacesAnim-over***")
  276. if(this.curSpaceObj.spaceId==cube.spaceId){//当前选中的空间才恢复颜色
  277. cube.instancedMeshIndexList.forEach(instanced=>{
  278. let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局Instance
  279. let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例
  280. let tmp = spaceInitMatrix.find(it=>it.index==_index);
  281. instancedMesh.instanceColor.needsUpdate = true;//打开颜色修改开关,不开,颜色是不能修改额
  282. instancedMesh.setColorAt(instanced.instancedAtIndex, tmp.color);//修改这个几何体的颜色
  283. });
  284. }
  285. });
  286. // 开始动画
  287. tween.start();
  288. this.tweenCameraAnmaChange(true)
  289. cube.position.x = curSpace.toPx;
  290. cube.position.z = curSpace.toPz;
  291. cube.scale.x = curSpace.toScaleX;
  292. cube.scale.z = curSpace.toScaleZ;
  293. }else{//有点问题规避下
  294. // 空间动画
  295. var tween = new TWEEN.Tween({
  296. x: scaleX,
  297. z: scaleZ,
  298. px:x,
  299. pz:z,
  300. })
  301. .to({
  302. x: toScaleX,
  303. z: toScaleZ,
  304. px:toPx,
  305. pz:toPz,
  306. }, 2000)
  307. .easing(TWEEN.Easing.Quadratic.InOut)
  308. .onUpdate((object)=> {
  309. cube.scale.x = object.x;
  310. cube.scale.z = object.z;
  311. cube.position.x = object.px;
  312. cube.position.z = object.pz;
  313. });
  314. // 开始动画
  315. tween.start();
  316. }
  317. // 屋顶尺寸调整
  318. const cubeRoof = this.gltfSpaceRoofs.find((item)=>{
  319. return item.userData.spaceId == curSpace.spaceId;
  320. })
  321. if(cubeRoof){
  322. let roofScaleX = curSpace.spaceObj.spaceWidth / cubeRoof.userData.spaceWidth
  323. let roofScaleY = curSpace.spaceObj.spaceHeight / cubeRoof.userData.spaceHeight
  324. // console.log("屋顶尺寸变化", curSpace.spaceObj, cubeRoof, roofScaleX, roofScaleY)
  325. cubeRoof.scale.set(roofScaleX,roofScaleY,1);//缩小为原来0.5倍
  326. cubeRoof.position.x = toPx;
  327. cubeRoof.position.z = toPz;
  328. }
  329. },
  330. //所有空间整体缩放-同时同步到数据里面
  331. allSpaceScale(){
  332. const centerOffset = new THREE.Vector3(0, 0, 0); // 假设中心点在几何体的正中心
  333. const scale = new THREE.Vector3(0.9, 1, 0.9); // 缩放尺度
  334. //实例化的默认中心是就原点也就是0 0 0 这个点,所以centerOffset也必须是原点
  335. // this.instancedMeshList.forEach((mesh)=>{
  336. // mesh.scale.copy(scale);
  337. // mesh.updateMatrix();
  338. // mesh.updateMatrixWorld();
  339. // mesh.updateMorphTargets();
  340. // mesh.geometry.computeVertexNormals();
  341. // })
  342. // this.instancedSpaceMeshList.forEach((mesh)=>{
  343. // mesh.scale.copy(scale);
  344. // mesh.updateMatrix();
  345. // mesh.updateMatrixWorld();
  346. // mesh.updateMorphTargets();
  347. // mesh.geometry.computeVertexNormals();
  348. // })
  349. // this.gltfLayouts.forEach((mesh)=>{
  350. // // mesh.scale.copy(scale);
  351. // })
  352. this.gltfSpaces.forEach(cube=>{
  353. let dis = centerOffset.clone().sub(cube.position);//获得差值向量
  354. // console.warn("***dis***",JSON.stringify(dis),JSON.stringify(cube.position),cube.spaceId);
  355. let x = dis.x * (1 - scale.x);//x轴需要移动的距离
  356. let y = dis.y * (1 - scale.y);//y轴需要移动的距离
  357. let z = dis.z * (1 - scale.z);//z轴需要移动的距离
  358. let pi = new THREE.Vector3();//新的中心点位置
  359. pi.x = cube.position.x + x;
  360. pi.y = cube.position.y + y;
  361. pi.z = cube.position.z + z;
  362. cube.scale.x = cube.scale.x * scale.x;//修改真实的比例
  363. cube.scale.z = cube.scale.z * scale.z;//
  364. cube.position.copy(pi);
  365. //同步信息到空间列表中
  366. let sapce = this.spaceList.find(it=>{ return it.spaceId==cube.spaceId});
  367. if(sapce){
  368. //更新空间中心点值
  369. sapce.centerX = pi.x * 100;
  370. sapce.centerY = -1* pi.z * 100;
  371. //更新空间宽度高度值
  372. sapce.spaceWidth = cube.scale.x * glbWidth;
  373. sapce.spaceHeight = cube.scale.z * glbHeight;
  374. }
  375. // let dir = new THREE.Vector3( 0, 10, 0 );;//当前几何体的位置参数
  376. // console.warn("***dis1***",JSON.stringify(pi),JSON.stringify(dir));
  377. // let length = 3;
  378. // let hex = 0xff0000;
  379. // let arrowHelper = new THREE.ArrowHelper( dir, pi, length, hex );
  380. // this.scene.add( arrowHelper );
  381. //这是矩阵运算处理缩放的方法-此为数学上的方案
  382. cube.instancedMeshIndexList.forEach(instanced=>{
  383. let _index = instanced.instancedMeshIndex;//第一个geometry实例 在 全局InstancedMesh实例的位置
  384. let instancedMesh = this.instancedSpaceMeshList[_index];//获取具体的网格实例
  385. let stratMatrix = new THREE.Matrix4();//定义一个四维矩阵
  386. instancedMesh.getMatrixAt(instanced.instancedAtIndex,stratMatrix);//获取当前几何体的四维矩阵到stratMatrix里面
  387. let scaleMatrix = new THREE.Matrix4().makeScale(scale.x, scale.y, scale.z);
  388. let inverseTranslationMatrix = new THREE.Matrix4().makeTranslation(x, y, z);
  389. stratMatrix.premultiply(inverseTranslationMatrix);
  390. stratMatrix.multiply(scaleMatrix);
  391. instancedMesh.instanceMatrix.needsUpdate = true;
  392. instancedMesh.setMatrixAt(instanced.instancedAtIndex,stratMatrix);//更新几何体的世界矩阵
  393. });
  394. })
  395. this.$nextTick(()=>{
  396. this.updateAllWallHandle();
  397. })
  398. setTimeout(() =>{
  399. this.updataPageData();//更新数据到各个对象里面
  400. this.calculateLayoutModelSize();//重新计算家具位置
  401. this.updateLables();//更新lable
  402. this.$nextTick(()=>{
  403. this.updateCareFul();//更新精细调整里面的空间
  404. this.updateSpanceData();//更新语音组件里面的空间
  405. })
  406. }, 100);
  407. }
  408. }
  409. }