既然大家需要3D会议室的demo源码,那我把其中一个vue文件代码发出来,很抱歉,最近有点抽不开时间,也没上来看过,先这样粗糙的给大家了,代码很糙,有问题欢迎指正,一起进步
<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));
},
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);
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;
},
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) {
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();
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();
},
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);
this.initDragControls([box]);
},
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;
}
},
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);
}
);
});
},
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;
}
},
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>