赞
踩
<template> <div class="second"> <div id="floorCanvas"> <div class="handle-btn"> <div class="create-mesh"> <button @click.stop="createObj('chair')"> <i>创建椅子</i> </button> <button @click.stop="createDDD"> <i>创建电梯</i> </button> <button @click.stop="createObj('table')" :disabled="addTable"> <i>创建桌子</i> </button> <button @click.stop="createObj('door')" :disabled="addDoor"> <i>创建门</i> </button> <button @click.stop="createObj('cool')"> <i>创建空调</i> </button> <button @click.stop="createGltf('flat')"> <i>创建会议平板</i> </button> <button @click.stop="inTheRoom"> <i>进入内部查看</i> </button> </div> </div> <div class="rotate-btn" v-show="rotateBtnShow" ref="rotateBtn"> <img src="../assets/run.png" alt="" /> </div> <div class="editDoor" v-if="editWellObject"> <button @click.stop="takeWellObject('east')">安置东墙</button> <button @click.stop="takeWellObject('south')">安置南墙</button> <button @click.stop="takeWellObject('west')">安置西墙</button> <button @click.stop="takeWellObject('north')">安置北墙</button> </div> </div> </div> </template> <script> import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import * as Stats from "three/examples/js/libs/stats.min.js"; import * as createMetry from "@/js/addMetry"; import { DragControls } from "three/examples/jsm/controls/DragControls"; import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader"; import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; export default { data() { return { floorCanvas: null, // 画布 animateAbled: false, // 是否实时追踪动画效应 rotateBtnShow: false, // 旋转按钮是否显示 addRotate: true, // 添加旋转开关 info: {}, // 模型信息 addTable: false, // 添加桌子开关 addDoor: false, // 添加门开关 editWellObject: false, // 修改门位置的四个按钮是否显示 // 基础尺寸(以地板大小为基数) baseX: 600, // 基础长 baseY: 1, // 基础高 baseZ: 240, // 基础宽 }; }, computed: { floorName() { return this.$route.query.name; }, }, mounted() { this.scene = null; // 场景 this.camera = null; // 相机 this.renderer = null; // 渲染器 this.stats = null; // 性能检测器 this.animateAbled = true; // 是否开启帧数动画检测 this.selectObject = null; // 鼠标点击选中的物体 this.dragControls = null; // 拖拽对象 this.cube = null; // 碰撞的对象 this.selectGroup = null; // 选中的组 this.init(); this.loader = new OBJLoader(); this.mat = new MTLLoader(); this.createFloor(); this.animate(); }, beforeDestroy() { // 清空设置,注销事件 this.scene = null; this.camera = null; this.renderer = null; this.stats = null; this.animateAbled = false; this.cube = null; this.dragControls = null; document.removeEventListener("click", this.mouseClick, false); document.removeEventListener("resize", this.setResize, false); }, methods: { // 创建地板 createFloor() { let that = this; let geometry = new THREE.BoxBufferGeometry( this.baseX, this.baseY, this.baseZ ); // 请求图片资源 this.textureLoader = new THREE.TextureLoader(); this.textureLoader.load("/floor.png", function (texture) { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.x = 10; texture.repeat.y = 5; let material = new THREE.MeshPhongMaterial({ map: texture, }); that.ground = new THREE.Mesh(geometry, material); that.ground.position.y = -60; that.scene.add(that.ground); }); }, // 创建墙 createWell(long, width, height, x, y, z, name) { let well = createMetry.addCubewall( long, height, width, 0, new THREE.MeshPhongMaterial({ color: 0xaaaaaa, opacity: 0.5, transparent: true, }), x, y, z, name ); this.scene.add(well); }, // 初始化 init() { this.floorCanvas = document.getElementById("floorCanvas"); this.initStats(); this.initScene(); this.initCamera(); this.initRenderer(); this.createFloor(); this.createWell( this.baseY, this.baseZ, 100, this.baseX / 2, -10, 0, "东墙" ); this.createWell( this.baseX, this.baseY, 100, 0, -10, -this.baseZ / 2, "南墙" ); this.createWell( this.baseY, this.baseZ, 100, -this.baseX / 2, -10, 0, "西墙" ); this.createWell( this.baseX, this.baseY, 100, 0, -10, this.baseZ / 2, "北墙" ); this.initLight(); this.initControls(); document.addEventListener("click", this.mouseClick, false); document.addEventListener("resize", this.setResize, false); }, // 进入房间内部 inTheRoom() { this.camera.position.set(-240, -60, 0); }, // 设置相机自适应屏幕大小 setResize() { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); }, // 初始化场景 initScene() { this.scene = new THREE.Scene(); this.scene.fog = new THREE.Fog(this.scene.background, 3000, 5000); }, // 初始化相机 initCamera() { this.camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 1000 ); this.camera.position.set(0, 120, 280); this.camera.lookAt(new THREE.Vector3(0, 0, 0)); // let helper = new THREE.CameraHelper(this.camera); // this.scene.add(helper); }, // 初始化灯光 initLight() { let directionalLight = new THREE.DirectionalLight(0xffffff, 0.3); //模拟远处类似太阳的光源 directionalLight.color.setHSL(0.1, 1, 0.95); directionalLight.position.set(0, 200, 0).normalize(); this.scene.add(directionalLight); let ambient = new THREE.AmbientLight(0xffffff, 1); //AmbientLight,影响整个场景的光源 ambient.position.set(0, 0, 0); this.scene.add(ambient); }, // 初始化性能插件 initStats() { let stats = new Stats(); stats.domElement.style.position = "absolute"; stats.domElement.style.left = "0px"; stats.domElement.style.top = "0px"; this.floorCanvas.appendChild(stats.domElement); this.stats = stats; }, // 初始化渲染器 initRenderer() { this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor(0x4682b4, 1.0); this.renderer.setPixelRatio(window.devicePixelRatio); this.floorCanvas.appendChild(this.renderer.domElement); }, // 初始化轨道控制器 initControls() { this.controls = new OrbitControls(this.camera, this.renderer.domElement); this.controls.enableDamping = true; this.controls.dampingFactor = 0.2; // 视角最小距离 this.controls.minDistance = 100; // 视角最远距离 this.controls.maxDistance = 5000; // 最大角度 this.controls.maxPolarAngle = Math.PI / 1.9; // this.controls.maxPolarAngle = 0; }, // 添加拖拽控件 initDragControls(objects, info) { let that = this; // 初始化拖拽控件 this.dragControls = new DragControls( objects, this.camera, this.renderer.domElement ); this.dragControls.transformGroup = true; // 开始拖拽 this.dragControls.addEventListener("dragstart", this.setDragstart); this.dragControls.addEventListener("drag", function (e) { // if (e.object.position.y < -60) { // e.object.position.y = -60; // } let xMax = that.baseX / 2 - info.sizeX / 2; let xMin = -that.baseX / 2 + info.sizeX / 2; let zMax = that.baseZ / 2 - info.sizeZ / 2; let zMin = -that.baseZ / 2 + info.sizeZ / 2; if (e.object.position.x >= xMax) { e.object.position.x = xMax; } if (e.object.position.x <= xMin) { e.object.position.x = xMin; } switch (info.position) { // 地板流 case "floor": if (e.object.position.z >= zMax) { e.object.position.z = zMax; } if (e.object.position.z <= zMin) { e.object.position.z = zMin; } e.object.position.y = -60; break; // 南墙 case "south": if (e.object.name === "门") { e.object.position.z = that.baseZ / 2 + 1; e.object.position.y = -60; } else if (e.object.name === "会议平板") { e.object.position.z = that.baseZ / 2 - 5; e.object.position.y = 0; } break; // 东墙 case "east": if (e.object.position.z >= zMax) { e.object.position.z = zMax; } if (e.object.position.z <= zMin) { e.object.position.z = zMin; } if (e.object.name === "门") { e.object.position.x = that.baseX / 2 + 1; e.object.position.y = -60; } else if (e.object.name === "会议平板") { e.object.position.x = that.baseX / 2 - 5; e.object.position.y = 0; } break; // 西墙 case "west": if (e.object.position.z >= zMax) { e.object.position.z = zMax; } if (e.object.position.z <= zMin) { e.object.position.z = zMin; } if (e.object.name === "门") { e.object.position.x = -that.baseX / 2 - 1; e.object.position.y = -60; } else if (e.object.name === "会议平板") { e.object.position.x = -that.baseX / 2 + 5; e.object.position.y = 0; } break; // 北墙 case "north": if (e.object.name === "门") { e.object.position.z = -that.baseZ / 2 - 1; e.object.position.y = -60; } else if (e.object.name === "会议平板") { e.object.position.z = -that.baseZ / 2 + 5; e.object.position.y = 0; } break; default: return; } }); // 拖拽结束 this.dragControls.addEventListener("dragend", this.setDragend); }, // 具名拖拽开始函数 setDragstart() { this.rotateBtnShow = false; this.editWellObject = false; this.controls.enabled = false; }, // 具名拖拽结束函数 setDragend() { this.controls.enabled = true; }, // 鼠标单击触发的事件 mouseClick(event) { let intersects = this.getIntersects(event); if (intersects !== {} && intersects.type === "Group") { this.selectObject = intersects; if (this.selectObject.name === "可旋转") { this.rotateBtnShow = true; this.addRotateClick(this.selectObject); } else { this.editWellObject = true; } } else { this.rotateBtnShow = false; this.selectObject = null; this.editWellObject = false; } }, // 获取与射线相交的对象数组 getIntersects(event) { event.preventDefault(); let raycaster = new THREE.Raycaster(); let mouse = new THREE.Vector2(); // console.log(mouse); mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置 raycaster.setFromCamera(mouse, this.camera); // 获取与射线相交的对象数组,其中的元素按照距离排序,越近的越靠前 let moudle = this.scene.children.filter((item) => { if (item.type === "Group") { return item; } }); let intersects = raycaster.intersectObjects(moudle, true); if (intersects.length > 0) { let object = intersects[0].object; this.getGroup(object); return this.selectGroup; } return {}; }, // 创建旋转按钮 createRotateBtn() { let halWidth = window.innerWidth / 2; let halHeight = window.innerHeight / 2; let vector = this.selectObject.position.clone().project(this.camera); this.$refs.rotateBtn.style.left = `${ halWidth + vector.x * halWidth - 40 }px`; this.$refs.rotateBtn.style.top = `${ -vector.y * halHeight + halHeight - this.selectObject.position.y - 230 }px`; }, // 添加旋转事件 addRotateClick() { if (this.addRotate) { this.addRotate = false; let that = this; let newX = Number(this.$refs.rotateBtn.style.left.replace("px", "")); // 数据格式转 document.addEventListener("dragenter ", function (e) { e.preventDefault(); }); document.addEventListener("dragover", function (e) { e.preventDefault(); }); // 旋转事件 this.$refs.rotateBtn.addEventListener( "drag", function (event) { if (event.x < newX) { that.selectObject.rotateY(Math.PI / 120); } else if (event.x > newX) { that.selectObject.rotateY(Math.PI / -120); } newX = event.x; }, true ); // 结束拖拽之后重新计算模型大小 this.$refs.rotateBtn.addEventListener( "dragend", function () { that.computeObjSize(that.selectObject); }, true ); } }, // 动画效应 animate() { if (!this.animateAbled) { return false; } if (this.selectObject) { this.createRotateBtn(); } requestAnimationFrame(this.animate); this.renderer.render(this.scene, this.camera); this.stats.update(); this.controls.update(); // if (this.cube && this.objects) { // this.pengPeng(); // } }, // 创建电梯 createDDD() { let x = Math.random() * 100; let z = Math.random() * 100; let box = createMetry.addCubewall( 30, 100, 30, 0, new THREE.MeshPhongMaterial({ color: 0xfda6d2, opacity: 1, transparent: true, }), x, -60, z, "楼层构造" ); this.scene.add(box); //创建box,并添加到场景 // this.cube = box; // this.originPoint = box.position.clone(); this.initDragControls([box]); }, // 碰撞检测 // pengPeng() { // for (let i = 0; i < this.cube.geometry.vertices.length; i++) { // let localVertex = this.cube.geometry.vertices[i].clone(); // let globalVertex = localVertex.applyMatrix4(this.cube.matrix); // let directionVector = globalVertex.sub(this.cube.position); // let ray = new THREE.Raycaster( // this.originPoint, // directionVector.clone().normalize() // ); // let collisionResults = ray.intersectObject(this.objects, true); // // console.log(collisionResults); // if ( // collisionResults.length > 0 && // collisionResults[0].distance < directionVector.length() // ) { // console.log("碰撞!"); // // this.dragControls.enabled = false; // } // } // }, // 创建导入外部OBJ文件 createObj(type) { switch (type) { // 创建桌子 case "table": this.addTable = true; this.info = { position: "floor" }; this.addObjItem( "/table/file.mtl", "/table/file.obj", [0, -60, 0], [0.05, 0.05, 0.05], "可旋转" ); break; // 创建椅子 case "chair": this.info = { position: "floor" }; this.addObjItem( "/chair/file.mtl", "/chair/file.obj", [-300, -60, 0], [0.5, 0.5, 0.5], "可旋转" ); break; // 创建门 case "door": this.addDoor = true; this.info = { position: "south" }; this.addObjItem( "/door/file.mtl", "/door/file.obj", [0, -60, this.baseZ / 2 + 1], [0.035, 0.035, 0.035], "门" ); break; // 创建空调 case "cool": this.info = { position: "floor" }; this.addObjItem( "/cool/file.mtl", "/cool/file.obj", [200, -60, 0], [0.55, 0.55, 0.55], "可旋转" ); break; default: return; } }, // 添加OBJ条件 addObjItem(mtlUrl, objUrl, position, scale, name) { let that = this; that.mat.load(mtlUrl, function (materials) { materials.preload(); that.loader.setMaterials(materials); that.loader.load( objUrl, function (object) { object.position.set(position[0], position[1], position[2]); object.scale.set(scale[0], scale[1], scale[2]); object.name = name; that.scene.add(object); // 计算模型长宽高 that.computeObjSize(object); } // function (xhr) { // console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); // }, // function (error) { // console.log(error); // } ); }); }, // 创建引入GLTF外部模型 createGltf(type) { switch (type) { case "flat": this.info = { position: "north" }; this.addGltfItem( "/flat/model.gltf", [0, 0, -this.baseZ / 2 + 5], [16, 12, 12], "会议平板" ); break; default: return; } }, // 添加GLTF条件 addGltfItem(url, position, scale, name) { let that = this; let loader = new GLTFLoader(); loader.load(url, function (gltf) { gltf.scene.position.set(position[0], position[1], position[2]); gltf.scene.scale.set(scale[0], scale[1], scale[2]); gltf.scene.name = name; that.scene.add(gltf.scene); that.computeObjSize(gltf.scene); }); }, // 更改墙上物品位置 takeWellObject(type) { switch (type) { case "east": if (this.selectObject.name === "门") { this.selectObject.rotation.y = Math.PI / 2; this.selectObject.position.set(this.baseX / 2 + 1, -60, 0); this.info.position = "east"; } else if (this.selectObject.name === "会议平板") { this.selectObject.rotation.y = Math.PI / -2; this.selectObject.position.set(this.baseX / 2 - 5, 0, 0); this.info.position = "east"; } this.computeObjSize(this.selectObject); break; case "south": if (this.selectObject.name === "门") { this.selectObject.rotation.y = 0; this.selectObject.position.set(0, -60, this.baseZ / 2 + 1); this.info.position = "south"; } else if (this.selectObject.name === "会议平板") { this.selectObject.rotation.y = Math.PI; this.selectObject.position.set(0, 0, this.baseZ / 2 - 5); this.info.position = "south"; } this.computeObjSize(this.selectObject); break; case "west": if (this.selectObject.name === "门") { this.selectObject.rotation.y = Math.PI / 2; this.selectObject.position.set(-this.baseX / 2 - 1, -60, 0); this.info.position = "west"; } else if (this.selectObject.name === "会议平板") { this.selectObject.rotation.y = Math.PI / 2; this.selectObject.position.set(-this.baseX / 2 + 5, 0, 0); this.info.position = "west"; } this.computeObjSize(this.selectObject); break; case "north": if (this.selectObject.name === "门") { this.selectObject.rotation.y = 0; this.selectObject.position.set(0, -60, -this.baseZ / 2 - 1); this.info.position = "north"; } else if (this.selectObject.name === "会议平板") { this.selectObject.rotation.y = 0; this.selectObject.position.set(0, 0, -this.baseZ / 2 + 5); this.info.position = "north"; } this.computeObjSize(this.selectObject); break; default: return; } }, // 计算当前模型的大小(长宽高) computeObjSize(object) { let boxSize = new THREE.Box3().setFromObject(object).getSize(); this.info.sizeX = parseInt(boxSize.x); this.info.sizeY = parseInt(boxSize.y); this.info.sizeZ = parseInt(boxSize.z); this.initDragControls([object], this.info); }, // 获取组 getGroup(intersects) { let selectGroup = intersects.parent; if (selectGroup.type === "Group") { return (this.selectGroup = selectGroup); } else { selectGroup = selectGroup.parent; this.getGroup(selectGroup); } }, }, }; </script> <style scoped lang="less"> i { font-style: normal; } #floorCanvas { position: relative; height: 100vh; width: 100vw; } .handle-btn { position: absolute; left: 0; top: 15%; button { width: 60px; padding: 5px; display: block; } button:hover { cursor: pointer; background-color: lightseagreen; color: #fff; } } .selected { background-color: tomato; color: #fff; font-weight: 600; } .create-mesh { margin-top: 20px; } .create-mesh button { width: 100px; } .create-mesh button span { width: 12px; height: 12px; display: inline-block; margin-left: 5px; } .rotate-btn { display: block; position: absolute; top: 20%; left: 30%; img { width: 50px; height: 50px; } } .editDoor { position: absolute; left: 50%; top: 70px; transform: translateX(-50%); button { font-style: 18px; padding: 5px; } button:hover { cursor: pointer; background-color: lightseagreen; color: #fff; } } </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。