three 同步导入glb模型,组合模型,点击模型弹窗
vue代码
实现效果为同步加载模型计算当前模型的Y轴高度,并且把模型按Y轴重叠在一起,点击可弹出对应消息弹框,弹框可随轨道控制器移动,并且窗口为自适应,
monitorThree

<template>
<div id="threeBox">
<div :style="cLheight" ref="three" id="ThreeId233"></div>
<!-- <div id="myStats"></div> -->
<a-card
id="modal"
size="small"
showDia
v-show="false"
:title="showModelDetail.bhaName"
style="width: 200px; height: 90px"
>
<p>型号:{{ showModelDetail.bhaModel }}</p>
<p>尺寸:{{ showModelDetail.bhaSpec }}</p>
<p>长度:{{ showModelDetail.bhaLength }}</p>
</a-card>
</div>
</template>
<script>
import * as THREE from "three";
import { BoxGeometry, MeshLambertMaterial, Scene } from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; // 轨道控制器 可以用鼠标进行放大缩小 调整方位
import * as Stats from "stats.js"; // 检测动画运行帧数
import * as dat from "dat.gui"; // datGui 可以为我们简化实验
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { selBhaDetail } from "@/services/monitor/index.js";
import {
CSS2DRenderer,
CSS2DObject,
} from "three/examples/jsm/renderers/CSS2DRenderer.js";
export default {
props: ["cLheight", "ThreeId", "sidebarItem", "bhaId2"],
data() {
return {
glftModel: "",
renderer: null,
close: true,
scene: null,
camera: null,
labelRenderer: null,
OrbitControlsAr: null,
showDia: false,
material: "",
y: 0,
group: null,
DirectionalLight: null,
label: null,
showModelDetail: {},
};
},
created() {
console.log("测试11", this.ThreeId, this.cLheight);
},
mounted() {
this.$nextTick(() => {
this.setThree();
this.add3dGlb();
this.monitorThree();
});
},
watch: {
bhaId2: {
handler(newValue, oldValue) {
console.log("测试????");
if(this.label != null) {
this.label.visible = false;
}
this.add3dGlb();
},
deep: true,
},
},
methods: {
setThree() {
let THIS = this;
this.scene = new THREE.Scene(); // 创建场景
// 场景雾化效果 第二个是雾化的近处值,第三个是雾化的远处值
// scene.fog = new THREE.Fog(0xff0000, 0.01, 100);
// 场景雾化效果 第一个是颜色,第二个是雾化浓度
// scene.fog = new THREE.FogExp2(0x0000ff,0.02)
// 强制让场景中的对象使用相同的材质
// scene.overrideMaterial = new THREE.MeshLambertMaterial({color:0x0000ff})
let container = document.getElementById("ThreeId233");
this.camera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
); // 创建透视摄像机
// let camera = new THREE.OrthographicCamera(window.innerWidth/-16,window.innerWidth/16,window.innerHeight/16,window.innerHeight/-16,-200,500)
this.renderer = new THREE.WebGLRenderer({
logarithmicDepthBuffer: true, // 对数深度缓存
antialias: true, // 抗锯齿
});
this.renderer.outputEncoding = THREE.sRGBEncoding;
// 设置渲染器的宽高
this.renderer.setSize(container.clientWidth, container.clientHeight);
this.renderer.shadowMap.enabled = true; // 如果要渲染阴影必须为真
// 把渲染器渲染好的dom,加入到我们创建的元素中
container.appendChild(this.renderer.domElement);
// let geometry = new THREE.BoxGeometry(8, 8, 8); // 创建一个立方体
// let material = new THREE.MeshLambertMaterial({ color: 0x00ff00 }); // 创建基础网格材质网格基础材质
// let cube = new THREE.Mesh(geometry, material); // 创建一个网格对象
// cube.position.y = 20
let axes = new THREE.AxesHelper(50); // 创建坐标轴
// scene.add(axes);
console.log("axes", axes);
this.camera.position.x = -1.5;
this.camera.position.y = 3; // 这里把相机拉高可以更直观的看到 光源显示 辅助线效果
this.camera.position.z = 0.1; // 设置相机的距离 Z 轴
// 创建地面网格对象 围成一个正方体
let planeGeometry = new THREE.PlaneGeometry(200, 200);
let planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
let plan = new THREE.Mesh(planeGeometry, planeMaterial);
plan.name = "方盒";
let plan2 = plan.clone();
let plan3 = plan.clone();
let plan4 = plan.clone();
let plan5 = plan.clone();
let plan6 = plan.clone();
plan2.position.z = -100;
plan2.position.y = 100;
plan2.position.x = 15;
plan3.rotation.y = 0.5 * Math.PI;
plan3.position.x = -85;
plan3.position.y = 100;
plan4.rotation.y = -0.5 * Math.PI;
plan4.position.x = 115;
plan4.position.y = 100;
plan5.rotation.x = 0.5 * Math.PI;
plan5.position.y = 200;
plan5.position.x = 15;
plan6.rotation.y = Math.PI;
plan6.position.y = 100;
plan6.position.z = 100;
plan6.position.x = 15;
plan.rotation.x = -0.5 * Math.PI;
plan.position.set(15, 0, 0);
plan.receiveShadow = true;
this.scene.add(plan);
this.scene.add(plan2);
this.scene.add(plan3);
this.scene.add(plan4);
this.scene.add(plan5);
this.scene.add(plan6);
//聚光灯光源
// 1.光照颜色
// 2.光照强度
// 3.光源发出光的最大距离
// 4.光线散射角度
// 5.聚光锥的半影衰减百分比
// 6.沿着光照距离的衰减量
this.DirectionalLight = new THREE.SpotLight(0xffffff);
this.DirectionalLight.size = 10;
this.DirectionalLight.castShadow = true;
this.DirectionalLight.angle = Math.PI / 2;
this.DirectionalLight.position.set(-20, 65, 45);
// spotLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
// spotLight.shadow.camera.far = 130;
// spotLight.shadow.camera.near = 40;
// 辅助对象
let DirectionalLightHelper = new THREE.SpotLightHelper(
this.DirectionalLight
);
// this.scene.add(DirectionalLightHelper);
let ambienLight = new THREE.AmbientLight(0xcccccc); // 基本光源 会照亮场景中所有物体不会产生阴影
this.scene.add(this.DirectionalLight);
this.scene.add(ambienLight);
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 220, 0);
this.scene.add(hemiLight);
///
THIS.labelRenderer = new CSS2DRenderer();
THIS.labelRenderer.setSize(container.clientWidth, container.clientHeight);
THIS.labelRenderer.domElement.style.position = "absolute";
THIS.labelRenderer.domElement.style.top = "0px";
THIS.labelRenderer.domElement.style.right = "0px";
let threeBox = document.getElementById("threeBox");
threeBox.appendChild(THIS.labelRenderer.domElement);
let ctrlObj = {};
console.log("labelRenderer", THIS.labelRenderer);
THIS.OrbitControlsAr = new OrbitControls(
THIS.camera,
THIS.labelRenderer.domElement
);
this.scene.add(THIS.OrbitControlsAr);
THIS.OrbitControlsAr.update();
renderScene();
// 监听参数变化, 渲染器重新渲染
function renderScene() {
if (THIS.close != false) {
requestAnimationFrame(renderScene); // stats 方法 启动
}
if (THIS.renderer != null) {
// stats.update();
if (THIS.glftModel) {
THIS.group.rotation.y += 0.01;
}
THIS.OrbitControlsAr.update();
THIS.renderer.render(THIS.scene, THIS.camera); // 渲染器 渲染
THIS.labelRenderer.render(THIS.scene, THIS.camera);
}
}
// 将检测动画运行帧数的界面添加到页面上
function addStats() {
let stats = new Stats();
stats.domElement.style.position = "absolute";
stats.domElement.style.left = "0px";
stats.domElement.style.top = "0px";
stats.setMode(0);
document.getElementById("myStats").appendChild(stats.domElement);
return stats;
}
// 监听页面变化 自适应场景
window.addEventListener("resize", onWindowResize, false);
function onWindowResize() {
let container22 = document.getElementById("ThreeId233");
THIS.camera.aspect = container22.clientWidth / container22.clientHeight;
// 更新相机的投影矩阵
THIS.camera.updateProjectionMatrix();
// 渲染器调用 重置 场景的渲染尺寸
THIS.renderer.setSize(
container22.clientWidth,
container22.clientHeight
);
THIS.labelRenderer.setSize(
container22.clientWidth,
container22.clientHeight
);
THIS.monitorThree();
}
},
async add3dGlb() {
let THIS = this;
if (THIS.label != null) {
console.log('label11',THIS.label)
// THIS.scene.remove(THIS.label);
}
let rows = await selBhaDetail(this.bhaId2);
let modelSort = [];
let arr = [];
if (rows.data.rows.length > 0) {
rows.data.rows.map((el) => {
arr.push(el.bhaName);
switch (true) {
case el.bhaName.indexOf("接头") != -1:
el.url = "static" + "/zjzh" + "/" + "接头" + ".glb";
modelSort.push(el);
break;
case el.bhaName.indexOf("钻杆") != -1:
el.url = "static" + "/zjzh" + "/" + "钻杆" + ".glb";
modelSort.push(el);
break;
case el.bhaName.indexOf("PDC") != -1:
el.url = "static" + "/zjzh" + "/" + "PDC钻头" + ".glb";
modelSort.push(el);
break;
case el.bhaName == "牙轮钻头":
el.url = "static" + "/zjzh" + "/" + "牙轮钻头" + ".glb";
modelSort.push(el);
break;
case el.bhaName.indexOf("钻铤") != -1:
if (el.bhaName == "无磁钻铤") {
el.url = "static" + "/zjzh" + "/" + "无磁钻铤" + ".glb";
modelSort.push(el);
} else if (el.bhaName.indexOf("钻铤") != -1) {
el.url = "static" + "/zjzh" + "/" + "无磁钻铤" + ".glb";
modelSort.push(el);
}
break;
case el.bhaName == "稳定器" || el.bhaName == "扶正器":
el.url = "static" + "/zjzh" + "/" + "扶正器" + ".glb";
modelSort.push(el);
break;
}
});
}
console.log("arr", arr);
console.log("modelSort", modelSort);
console.log("row", rows);
let loader = new GLTFLoader();
let dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("static/"); // 这个是加载draco算法,这样才能解析压缩后的gltf模型格式.
loader.setDRACOLoader(dracoLoader);
let url = "";
console.log("this.sidebarItem", this.sidebarItem);
let data3dFile = [
{
url: "static/zjzh/扶正器.glb",
},
{
url: "static/zjzh/接头.glb",
},
{
url: "static/zjzh/螺杆.glb",
},
{
url: "static/zjzh/套洗头.glb",
},
{
url: "static/zjzh/无磁钻铤.glb",
},
{
url: "static/zjzh/牙轮钻头.glb",
},
{
url: "static/zjzh/钻铤.glb",
},
{
url: "static/zjzh/钻杆.glb",
},
{
url: "static/zjzh/PDC钻头.glb",
},
// {
// url: "static/三闸板.glb",
// },
];
THIS.group = new THREE.Group();
for (let x = 0; x < modelSort.length; x++) {
let gltf = await loader.loadAsync(modelSort[x].url);
//初始展示的模型的缩放比例
gltf.scene.scale.set(1, 1, 1);
gltf.scene.name = `ceshi${x}`;
gltf.scene.userData = modelSort[x];
THIS.glftModel = gltf;
gltf.scene.traverse(function (child) {
if (child.isMesh) {
// console.log(child,999)
child.frustumCulled = false;
//模型阴影
child.castShadow = true;
//模型自发光
// child.material.emissive = new THREE.Color(0x000000);
// child.material.roughness = 0;
// child.material.metalness = 1;
// child.material.emissiveMap = child.material.map;
}
});
let box = new THREE.Box3().setFromObject(gltf.scene);
console.log("box", box);
if (x == 0) {
gltf.scene.position.set(0, 1, 0);
THIS.y = box.max.y + 1;
// const geometry = new THREE.CylinderGeometry(1, 1, box.max.y, 32);
// const material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
// const cylinder = new THREE.Mesh(geometry, material);
// cylinder.position.set(0, THIS.y, 0);
// cylinder.name = x + '圆柱体'
// THIS.scene.add(cylinder);
} else {
gltf.scene.position.set(0, THIS.y, 0);
THIS.y += box.max.y;
// const geometry = new THREE.CylinderGeometry(1, 1, box.max.y, 32);
// const material = new THREE.MeshBasicMaterial({ color: x % 2 != 0 ? 0xffff00 : 0xff00ff });
// material.transparent = true
// material.opacity = 0.5
// const cylinder = new THREE.Mesh(geometry, material);
// cylinder.name = x + '圆柱体'
// cylinder.position.set(0, THIS.y, 0);
// THIS.scene.add(cylinder);
}
THIS.group.add(gltf.scene);
if (x == modelSort.length - 1) {
console.log("THIS.group", THIS.group);
if (THIS.group.children.length > 0) {
THIS.group.children;
}
THIS.OrbitControlsAr.minPolarAngle = Math.PI / 2;
THIS.OrbitControlsAr.maxPolarAngle = Math.PI / 2;
THIS.OrbitControlsAr.minDistance = 0.5;
THIS.OrbitControlsAr.maxDistance = 20;
const a = new THREE.Vector3(0, 2, 0);
THIS.OrbitControlsAr.target = a; // 把相机对准某个点
THIS.group.name = "钻具组合";
let findObj = THIS.scene.getObjectByName("钻具组合", false);
THIS.scene.remove(findObj);
THIS.scene.add(THIS.group);
THIS.DirectionalLight.position.y = THIS.y / 2;
THIS.DirectionalLight.target = THIS.group;
}
}
console.log("group", THIS.group);
},
monitorThree() {
let THIS = this;
let dom = THIS.renderer.domElement;
// let dom = document.getElementById(THIS.ThreeId);
THIS.labelRenderer.domElement.removeEventListener(
"click",
zzclickZj,
false
);
let rect = THIS.labelRenderer.domElement.getBoundingClientRect();
THIS.labelRenderer.domElement.addEventListener("click", zzclickZj, false);
function zzclickZj() {
console.log("dom", THIS.labelRenderer);
const { clientX, clientY } = event;
console.log("clientX", [window], event, clientX, clientY);
let x = ((clientX - rect.left) / rect.width) * 2 - 1;
let y = -((clientY - rect.top) / rect.height) * 2 + 1;
const mousePoint = new THREE.Vector2(x, y);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mousePoint, THIS.camera);
console.log("THIS.scene", THIS.scene, THIS.camera);
const intersects = raycaster.intersectObjects(
THIS.scene.children,
true
);
console.log("intersects111", intersects);
const intersect = intersects.filter(
(item) =>
item.object.name != "方盒" &&
!(item.object instanceof THREE.AxesHelper)
);
console.log("inser", intersect, intersect.length);
if (intersect.length > 0) {
THIS.showModelDetail = intersect[0].object.parent.userData;
THIS.showDetailDia(intersect[0].point);
} else {
this.showDia = false;
THIS.showDetailDia("空");
}
}
},
// 显示弹窗
showDetailDia(mousePoint) {
let THIS = this;
console.log("mousePoint", mousePoint);
this.showDia = true;
this.$nextTick(() => {
if (mousePoint == "空") {
if (THIS.label != null) {
THIS.label.visible = false;
}
} else {
let testDiv = document.getElementById("modal");
THIS.label = new CSS2DObject(testDiv);
THIS.label.position.set(mousePoint.x, mousePoint.y, 0);
// mesh.add(label);
this.scene.add(THIS.label);
}
});
},
clearRenderer() {
let THIS = this;
THIS.scene.remove(THIS.label);
this.close = false;
// let dom = document.querySelector("#three");
// // 把渲染器渲染好的dom,加入到我们创建的元素中
// dom.removeChild(this.renderer.domElement);
// this.glftModel.geometry.dispose();
// this.glftModel.material.dispose();
if (this.renderer) {
this.renderer.dispose();
this.renderer.forceContextLoss();
this.renderer.context = null;
this.renderer.domElement = null;
this.renderer = null;
}
},
},
beforeDestroy() {
console.log("销毁");
this.clearRenderer();
},
};
</script>
<style lang="less" scoped>
/deep/ #modal {
height: 190px !important;
}
/deep/ .ant-card-body {
height: 190px !important;
}
</style>
1671

被折叠的 条评论
为什么被折叠?



