|
|
@@ -0,0 +1,663 @@
|
|
|
+import { WebGLRenderer, Scene, PerspectiveCamera, Vector3, MOUSE, Raycaster, Vector2, } from "@three/Three.js";
|
|
|
+import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer';
|
|
|
+import { OrbitControls } from "@three/examples/jsm/controls/OrbitControls";
|
|
|
+import { TransformControls } from "@three/examples/jsm/controls/TransformControls";
|
|
|
+import { GUI } from "@three/examples/jsm/libs/lil-gui.module.min.js";
|
|
|
+
|
|
|
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
|
|
+import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
|
|
+import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
|
|
|
+import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
|
|
|
+import { TDSLoader } from "three/examples/jsm/loaders/TDSLoader";
|
|
|
+import { MTLLoader } from "three-obj-mtl-loader";
|
|
|
+
|
|
|
+import { createSprite } from "./ThreeLabel";
|
|
|
+
|
|
|
+import { getColor } from "./ThreeColor";
|
|
|
+import WebGL from "./WebGL";
|
|
|
+
|
|
|
+import * as THREE from "@three/Three.js";
|
|
|
+import TWEEN from "@tweenjs/tween.js";
|
|
|
+import store from "../../../store/index.js";
|
|
|
+
|
|
|
+import { compress, deCompress } from "@tools/zipString.js";
|
|
|
+
|
|
|
+import BASE from "@tools/basicTool.js";
|
|
|
+
|
|
|
+THREE.Cache.enabled = true;
|
|
|
+
|
|
|
+export class TEngine {
|
|
|
+ // 挂载的 DOM
|
|
|
+ dom = null;
|
|
|
+ // 场景
|
|
|
+ scene = null;
|
|
|
+ // 相机
|
|
|
+ camera = null;
|
|
|
+
|
|
|
+ renderer = null;
|
|
|
+
|
|
|
+ GUI = null;
|
|
|
+
|
|
|
+ css3DRenderer = null;
|
|
|
+
|
|
|
+ orbitControls = null;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 向场景中添加模型
|
|
|
+ * @param {...any} object 模型列表
|
|
|
+ */
|
|
|
+ // addObject(...object) {
|
|
|
+ // object.forEach(ele => {
|
|
|
+ // this.scene.beforeAdd(ele);
|
|
|
+ // this.scene.add(ele);
|
|
|
+ // this.scene.afterAdd(ele);
|
|
|
+ // });
|
|
|
+ // };
|
|
|
+ addObject(modelArray) {
|
|
|
+ modelArray.forEach(ele => {
|
|
|
+ this.scene.beforeAdd(ele);
|
|
|
+ this.scene.add(ele);
|
|
|
+ this.scene.afterAdd(ele);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ getScene() {
|
|
|
+ return this.scene;
|
|
|
+ };
|
|
|
+
|
|
|
+ getCamera() {
|
|
|
+ return this.camera;
|
|
|
+ };
|
|
|
+
|
|
|
+ getRenderer() {
|
|
|
+ return this.renderer;
|
|
|
+ };
|
|
|
+
|
|
|
+ getGUI() {
|
|
|
+ return this.GUI;
|
|
|
+ }
|
|
|
+
|
|
|
+ getCss3DRenderer() {
|
|
|
+ return this.css3DRenderer;
|
|
|
+ }
|
|
|
+
|
|
|
+ getOrbitControls() {
|
|
|
+ return this.orbitControls;
|
|
|
+ }
|
|
|
+
|
|
|
+ exportScene(scene) {
|
|
|
+ // 将场景中的对象转换为JSON格式的数据
|
|
|
+ let jsonData = scene.toJSON();
|
|
|
+
|
|
|
+ const hasFloor = scene.children.some(ele => {
|
|
|
+ return ele.name === "网格地板" && ele.disalbedDelete
|
|
|
+ });
|
|
|
+
|
|
|
+ jsonData.hasFloor = hasFloor;
|
|
|
+ let sceneData = JSON.stringify({ hasFloor, sceneData: jsonData });
|
|
|
+ // 保存JSON数据到文件中
|
|
|
+ // 这里可以使用浏览器的文件API进行保存操作
|
|
|
+ // 这里只是一个简单的示例
|
|
|
+ let blob = new Blob([sceneData], { type: 'application/json' });
|
|
|
+ let url = URL.createObjectURL(blob);
|
|
|
+ let a = document.createElement('a');
|
|
|
+ a.href = url;
|
|
|
+ a.download = '3D场景文件.json';
|
|
|
+ a.click();
|
|
|
+ }
|
|
|
+
|
|
|
+ importScene(jsonData) {
|
|
|
+ // 解析JSON数据
|
|
|
+ const data = JSON.parse(jsonData);
|
|
|
+
|
|
|
+ // 根据解析的数据创建 Three.js 对象
|
|
|
+ const loader = new THREE.ObjectLoader();
|
|
|
+ const importedObjects = loader.parse(data.sceneData);
|
|
|
+ this.scene?.children?.forEach(ele => {
|
|
|
+ this.scene.remove(ele);
|
|
|
+ });
|
|
|
+
|
|
|
+ if (data.hasFloor) {
|
|
|
+ const gridHelper = new THREE.GridHelper(2000, 100, getColor("#888"), getColor("#888"));
|
|
|
+ gridHelper.name = "网格地板";
|
|
|
+ gridHelper.disalbedDelete = true;
|
|
|
+ this.scene.add(gridHelper);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加对象到场景中
|
|
|
+ this.scene.add(importedObjects);
|
|
|
+ }
|
|
|
+
|
|
|
+ constructor(ThreeEngine = {}) {
|
|
|
+ // 场景
|
|
|
+ const scene = new Scene();
|
|
|
+ scene.beforeAdd = ThreeEngine.beforeAdd || scene.beforeAdd;
|
|
|
+ scene.afterAdd = ThreeEngine.afterAdd || scene.afterAdd;
|
|
|
+
|
|
|
+ // scene.background = getColor("#1890ff");
|
|
|
+ scene.background = new THREE.Color(0xadd8e6);
|
|
|
+
|
|
|
+ // 相机
|
|
|
+ const camera = new PerspectiveCamera(
|
|
|
+ // 视野角度(fov)
|
|
|
+ ThreeEngine?.cameraOptions?.fov || 75,
|
|
|
+ // 长宽比(aspect ratio)
|
|
|
+ ThreeEngine?.cameraOptions?.aspectRatio || window.innerWidth / window.innerHeight,
|
|
|
+ // 近截面(near)
|
|
|
+ ThreeEngine?.cameraOptions?.near || 0.1,
|
|
|
+ // 远截面(far)
|
|
|
+ ThreeEngine?.cameraOptions?.far || 1000
|
|
|
+ );
|
|
|
+
|
|
|
+ // 设置摄像机位置和朝向
|
|
|
+ camera.position.set(80, 100, 200); // 调整摄像机位置
|
|
|
+ camera.lookAt(new THREE.Vector3(0, 0, 0)); // 设置摄像机朝向场景中心
|
|
|
+
|
|
|
+ // 渲染器
|
|
|
+ const renderer = new WebGLRenderer({
|
|
|
+ antialias: true, // 开启抗锯齿
|
|
|
+ });
|
|
|
+ // renderer.setSize(window.innerWidth, window.innerHeight);
|
|
|
+ const rendererBox = getComputedStyle(ThreeEngine.dom, null);
|
|
|
+ const renderSizeWidth = parseFloat(rendererBox.width);
|
|
|
+ const renderSizeHeight = parseFloat(rendererBox.height);
|
|
|
+ renderer.setSize(renderSizeWidth, renderSizeHeight);
|
|
|
+
|
|
|
+ ThreeEngine?.dom?.appendChild(renderer.domElement);
|
|
|
+
|
|
|
+ const css3DRenderer = new CSS3DRenderer();
|
|
|
+ css3DRenderer.setSize(renderSizeWidth, renderSizeHeight);
|
|
|
+ css3DRenderer.render(scene, camera);
|
|
|
+ css3DRenderer.domElement.style.position = 'absolute';
|
|
|
+ css3DRenderer.domElement.style.top = 0;
|
|
|
+ css3DRenderer.domElement.style.pointerEvents = 'none';
|
|
|
+ ThreeEngine.dom.appendChild(css3DRenderer.domElement);
|
|
|
+
|
|
|
+ ThreeEngine.dom.addEventListener("drop", (e) => {
|
|
|
+ // 获取拖拽的数据
|
|
|
+ const modelInfo = JSON.parse(e.dataTransfer.getData("setModel"));
|
|
|
+ this.addModel(modelInfo);
|
|
|
+ e.preventDefault();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 设置鼠标功能键(轨道控制器)
|
|
|
+ const orbitControls = new OrbitControls(camera, renderer.domElement);
|
|
|
+ // 移动带阻尼
|
|
|
+ orbitControls.enableDamping = ThreeEngine.enableDamping || false;
|
|
|
+ // 设置阻尼系数
|
|
|
+ orbitControls.dampingFactor = ThreeEngine.dampingFactor || 0;
|
|
|
+ // 自动旋转
|
|
|
+ orbitControls.autoRotate = ThreeEngine.autoRotate || false;
|
|
|
+
|
|
|
+ orbitControls.mouseButtons = {
|
|
|
+ LEFT: null, // 左键无功能
|
|
|
+ MIDDLE: MOUSE.PAN, // 中键移动
|
|
|
+ RIGHT: MOUSE.ROTATE // 右键旋转
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ThreeEngine.showGridHelper) {
|
|
|
+ // 底图网格 (总长宽,分多少个网格,颜色,轴线颜色,和网格颜色 #e6e8ed)
|
|
|
+ const gridHelper = new THREE.GridHelper(2000, 100, getColor("#888"), getColor("#888"));
|
|
|
+ gridHelper.name = "网格地板";
|
|
|
+ gridHelper.disalbedDelete = true;
|
|
|
+ scene.add(gridHelper);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化射线发射器
|
|
|
+ let raycaster = new Raycaster();
|
|
|
+ // 初始化变换控制器
|
|
|
+ let transformControls = new TransformControls(camera, renderer.domElement);
|
|
|
+ // 将变换控制器添加至场景
|
|
|
+ scene.add(transformControls);
|
|
|
+
|
|
|
+ let transing = false;
|
|
|
+ transformControls.addEventListener("mouseDown", event => {
|
|
|
+ transing = true;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 初始化鼠标位置
|
|
|
+ let mouse = new Vector2();
|
|
|
+ let x = 0;
|
|
|
+ let y = 0;
|
|
|
+ let width = 0;
|
|
|
+ let height = 0;
|
|
|
+
|
|
|
+ renderer.domElement.addEventListener("mousemove", event => {
|
|
|
+ x = event.offsetX;
|
|
|
+ y = event.offsetY;
|
|
|
+ width = renderer.domElement.offsetWidth;
|
|
|
+ height = renderer.domElement.offsetHeight;
|
|
|
+ mouse.x = x / width * 2 - 1;
|
|
|
+ mouse.y = -y * 2 / height + 1;
|
|
|
+
|
|
|
+ // 配置射线发射器
|
|
|
+ raycaster.setFromCamera(mouse, camera)
|
|
|
+ // 移除变换控制器
|
|
|
+ // scene.remove(transformControls);
|
|
|
+ const intersection = raycaster.intersectObjects(scene.children);
|
|
|
+ if (intersection.length) {
|
|
|
+ const object = intersection[0].object;
|
|
|
+ // 如果当前物体不等于缓存的物体
|
|
|
+ if (object !== this.cacheObject) {
|
|
|
+ // 如果有缓存物体先执行之前物体的离开事件
|
|
|
+ if (this.cacheObject) {
|
|
|
+ this.cacheObject.dispatchEvent({
|
|
|
+ type: "mouseleave"
|
|
|
+ });
|
|
|
+ }
|
|
|
+ object.dispatchEvent({ // 添加当前物体进入事件
|
|
|
+ type: "mouseenter"
|
|
|
+ });
|
|
|
+ } else if (object === this.cacheObject) { // 如果当前物体等于缓存的物体
|
|
|
+ // 执行移动事件
|
|
|
+ object.dispatchEvent({
|
|
|
+ type: "mousemove"
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.cacheObject = object;
|
|
|
+ } else {
|
|
|
+ // 如果有缓存物体就先执行离开事件
|
|
|
+ if (this.cacheObject) {
|
|
|
+ this.cacheObject.dispatchEvent({
|
|
|
+ type: "mouseleave"
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.cacheObject = null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 点击事件
|
|
|
+ if (!ThreeEngine.disabledMouseEvent) {
|
|
|
+ renderer.domElement.addEventListener("click", event => {
|
|
|
+ if (ThreeEngine.click) {
|
|
|
+ // 移除变换控制器
|
|
|
+ scene.remove(transformControls);
|
|
|
+ // 停用变换控制器
|
|
|
+ transformControls.enabled = false;
|
|
|
+ // 配置射线发射器,传递鼠标和相机对象
|
|
|
+ raycaster.setFromCamera(mouse, camera);
|
|
|
+ // 获取射线发射器捕获的模型列表,传进去场景中所以模型,穿透的会返回我们
|
|
|
+ const intersection = raycaster.intersectObjects(scene.children);
|
|
|
+ ThreeEngine.click(intersection?.[0]?.object || {}, intersection);
|
|
|
+ } else {
|
|
|
+ if (transing) {
|
|
|
+ transing = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 移除变换控制器
|
|
|
+ scene.remove(transformControls);
|
|
|
+ // 停用变换控制器
|
|
|
+ transformControls.enabled = false;
|
|
|
+ // 配置射线发射器,传递鼠标和相机对象
|
|
|
+ raycaster.setFromCamera(mouse, camera);
|
|
|
+ // 获取射线发射器捕获的模型列表,传进去场景中所以模型,穿透的会返回我们
|
|
|
+ const intersection = raycaster.intersectObjects(scene.children);
|
|
|
+ let object = {};
|
|
|
+ if (intersection.length) {
|
|
|
+ const baseType = ["Scene", "GridHelper", "DirectionalLight", "TransformControls", "AxesHelper"];
|
|
|
+ const isBaseType = baseType.some(ele => {
|
|
|
+ return ele === intersection[0].object.type;
|
|
|
+ });
|
|
|
+ if (!isBaseType) {
|
|
|
+ const shiftKey = event.shiftKey;
|
|
|
+ // 获取第一个模型
|
|
|
+ if (shiftKey) {
|
|
|
+ object = this.getRootModel(intersection[0].object);
|
|
|
+ } else {
|
|
|
+ object = this.getRootMaterialObject(intersection[0].object);
|
|
|
+ }
|
|
|
+ // 添加变换控制器
|
|
|
+ scene.add(transformControls);
|
|
|
+ // 启用变换控制器
|
|
|
+ transformControls.enabled = true;
|
|
|
+ transformControls.attach(object);
|
|
|
+ ThreeEngine.selectModel && ThreeEngine.selectModel(object);
|
|
|
+ } else {
|
|
|
+ ThreeEngine.selectModel && ThreeEngine.selectModel(object);
|
|
|
+ }
|
|
|
+ localStorage.setItem("viewModel", this.getRootModel(object)?.userData?.modelName);
|
|
|
+ } else {
|
|
|
+ ThreeEngine.selectModel && ThreeEngine.selectModel(null);
|
|
|
+ localStorage.setItem("viewModel", JSON.stringify({}));
|
|
|
+ }
|
|
|
+ store.commit("setActiveModel", object || {});
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听变换控制器模式更改
|
|
|
+ document.addEventListener("keyup", event => {
|
|
|
+ const shiftKey = event.shiftKey;
|
|
|
+ // 变换控制器为启用状态执行
|
|
|
+ if (transformControls.enabled) {
|
|
|
+ // 鼠标按下e键,模式改为缩放
|
|
|
+ if (event.key === "s") {
|
|
|
+ transformControls.mode = "scale";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 鼠标按下r键,模式改为旋转
|
|
|
+ if (event.key === "r") {
|
|
|
+ transformControls.mode = "rotate";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 鼠标按下t键,模式改为平移
|
|
|
+ if (event.key === "t") {
|
|
|
+ transformControls.mode = "translate";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 按下 Delete ,删除选中模型的选中部分,按下 Shift + Delete,删除选中模型整体
|
|
|
+ if (event.key === "Delete" && store.state.activeModel.id && !store.state.activeModel.disalbedDelete) {
|
|
|
+ if (shiftKey) {
|
|
|
+ const rootModel = this.getRootModel(store.state.activeModel);
|
|
|
+ rootModel.geometry?.dispose();
|
|
|
+ rootModel.material?.dispose();
|
|
|
+ rootModel?.parent?.remove(rootModel);
|
|
|
+ } else {
|
|
|
+ const modelParentNode = store.state.activeModel.parent;
|
|
|
+ store.state.activeModel.geometry?.dispose();
|
|
|
+ store.state.activeModel.material?.dispose();
|
|
|
+ modelParentNode.remove(store.state.activeModel);
|
|
|
+ }
|
|
|
+ scene.children.forEach(ele => {
|
|
|
+ if (ele.isTransformControls) {
|
|
|
+ scene.remove(ele);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ renderer.render(scene, camera);
|
|
|
+ store.commit("setActiveModel", {});
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 添加事件监听器
|
|
|
+ transformControls.addEventListener("mouseUp", () => {
|
|
|
+ ThreeEngine.modelValueChange && ThreeEngine.modelValueChange(this.activeModel);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ const animate = () => {
|
|
|
+ orbitControls.update();
|
|
|
+ css3DRenderer.render(scene, camera);
|
|
|
+ requestAnimationFrame(animate);
|
|
|
+ renderer.render(scene, camera);
|
|
|
+ TWEEN.update();
|
|
|
+ };
|
|
|
+
|
|
|
+ if (WebGL.isWebGLAvailable()) {
|
|
|
+ animate();
|
|
|
+ window.addEventListener("resize", () => {
|
|
|
+ renderer.setSize(window.innerWidth, window.innerHeight);
|
|
|
+ camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
+ camera.updateProjectionMatrix();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ const warning = WebGL.getWebGLErrorMessage();
|
|
|
+ ThreeEngine?.dom?.appendChild(warning);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ThreeEngine.showGUI) {
|
|
|
+ this.GUI = new GUI();
|
|
|
+
|
|
|
+ const eventObject = {
|
|
|
+ requestFullscreen() {
|
|
|
+ document.body.requestFullscreen();
|
|
|
+ },
|
|
|
+ exitFullscreen() {
|
|
|
+ if (document.fullscreenElement) {
|
|
|
+ document.exitFullscreen();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const folder = this.GUI.addFolder("屏幕操作");
|
|
|
+
|
|
|
+ folder.add(eventObject, "requestFullscreen").name("全屏展示");
|
|
|
+ folder.add(eventObject, "exitFullscreen").name("退出全屏");
|
|
|
+ }
|
|
|
+
|
|
|
+ this.dom = ThreeEngine?.dom;
|
|
|
+ this.scene = scene;
|
|
|
+ this.camera = camera;
|
|
|
+ this.renderer = renderer;
|
|
|
+ this.css3DRenderer = css3DRenderer;
|
|
|
+ this.orbitControls = orbitControls;
|
|
|
+
|
|
|
+ this.fileLoaderMap = {
|
|
|
+ "glb": new GLTFLoader(),
|
|
|
+ "gltf": new GLTFLoader(),
|
|
|
+ "obj": new OBJLoader(),
|
|
|
+ "fbx": new FBXLoader(),
|
|
|
+ "3ds": new TDSLoader(),
|
|
|
+ }
|
|
|
+
|
|
|
+ // 读取模型
|
|
|
+ this.loadModel = ({ fileType = "glb", dirName, filePath, materialsPath }) => {
|
|
|
+ if (materialsPath) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const mtlLoader = new MTLLoader();
|
|
|
+ mtlLoader.load(materialsPath, (materials) => {
|
|
|
+ materials.preload();
|
|
|
+
|
|
|
+ const loader = this.fileLoaderMap[fileType];
|
|
|
+ loader.setMaterials(materials);
|
|
|
+ loader.load(filePath, (result) => {
|
|
|
+ switch (fileType) {
|
|
|
+ case "glb":
|
|
|
+ resolve(result.scene);
|
|
|
+ break;
|
|
|
+ case "gltf":
|
|
|
+ resolve(result.scene);
|
|
|
+ break;
|
|
|
+ case "fbx":
|
|
|
+ resolve(result);
|
|
|
+ break;
|
|
|
+ case "obj":
|
|
|
+ resolve(result);
|
|
|
+ break;
|
|
|
+ case "3ds":
|
|
|
+ resolve(result);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ resolve({});
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const loader = this.fileLoaderMap[fileType];
|
|
|
+ loader.setResourcePath(`./static/model/${fileType}/${dirName}/`);
|
|
|
+ loader.load(filePath, (result) => {
|
|
|
+ switch (fileType) {
|
|
|
+ case "glb":
|
|
|
+ resolve(result.scene);
|
|
|
+ break;
|
|
|
+ case "gltf":
|
|
|
+ resolve(result.scene);
|
|
|
+ break;
|
|
|
+ case "fbx":
|
|
|
+ resolve(result);
|
|
|
+ break;
|
|
|
+ case "obj":
|
|
|
+ resolve(result);
|
|
|
+ break;
|
|
|
+ case "3ds":
|
|
|
+ resolve(result);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ resolve({});
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 添加模型
|
|
|
+ this.addModel = (modelInfo) => {
|
|
|
+
|
|
|
+ BASE.showLoading({
|
|
|
+ text: "加载模型中...请稍后..."
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ // 首先检查缓存中是否已经有该模型数据
|
|
|
+ const cachedData = THREE.Cache.get(modelInfo.userData.modelName);
|
|
|
+ if (cachedData !== undefined) {
|
|
|
+ this.addObject([cachedData]);
|
|
|
+ BASE.closeLoading();
|
|
|
+ } else {
|
|
|
+ this.loadModel({
|
|
|
+ fileType: modelInfo.modelType,
|
|
|
+ dirName: modelInfo.modelName,
|
|
|
+ filePath: `./static/model/${modelInfo.modelType}/${modelInfo.modelName}/model.${modelInfo.modelType}`,
|
|
|
+ }).then((sourceModel) => {
|
|
|
+ let model = sourceModel;
|
|
|
+ if (modelInfo.userData) {
|
|
|
+ model.userData = Object.assign({ isRootModel: true }, model.userData, modelInfo.userData);
|
|
|
+ }
|
|
|
+
|
|
|
+ model.children.forEach((item) => {
|
|
|
+ item.castShadow = true;
|
|
|
+ item.receiveShadow = true;
|
|
|
+
|
|
|
+ // const material = new THREE.MeshStandardMaterial({
|
|
|
+ // transparent: false,
|
|
|
+ // opacity: 1,
|
|
|
+ // });
|
|
|
+ // item.material = material;
|
|
|
+ });
|
|
|
+
|
|
|
+ const baseBoxSize = new THREE.Vector3(100, 100, 100);
|
|
|
+ // 计算模型的包围盒(bounding box)
|
|
|
+ const boundingBox = new THREE.Box3().setFromObject(model);
|
|
|
+ const modelSize = new THREE.Vector3();
|
|
|
+ boundingBox.getSize(modelSize);
|
|
|
+
|
|
|
+ // 计算模型的最大尺寸
|
|
|
+ const maxModelSize = Math.max(modelSize.x, modelSize.y, modelSize.z);
|
|
|
+
|
|
|
+ // 计算缩放比例,使模型尺寸不超过基准盒子的尺寸
|
|
|
+ const scaleFactor = baseBoxSize.clone().divideScalar(maxModelSize);
|
|
|
+
|
|
|
+ // 将模型等比例缩放到适应基准盒子大小
|
|
|
+ model.scale.set(scaleFactor.x, scaleFactor.y, scaleFactor.z);
|
|
|
+
|
|
|
+ camera.position.z = 200;
|
|
|
+
|
|
|
+ // const modelLabelArray = createLabel([
|
|
|
+ // { text: "测试机柜" }
|
|
|
+ // ]);
|
|
|
+ // modelLabelArray.forEach(ele => {
|
|
|
+ // model.add(ele);
|
|
|
+ // });
|
|
|
+
|
|
|
+
|
|
|
+ // const labelDiv = this.createDom();
|
|
|
+ // labelDiv.innerHTML = "风机主体";
|
|
|
+ // let css3DObject = new CSS3DObject(labelDiv);
|
|
|
+ // css3DObject.position.set(...positionInfo)
|
|
|
+ // css3DObject.scale.set(0.03, 0.03, 0.03);
|
|
|
+ // model.add(css3DObject);
|
|
|
+
|
|
|
+
|
|
|
+ model = this.formateModel(modelInfo, model);
|
|
|
+ // createSprite(model);
|
|
|
+ THREE.Cache.add(modelInfo.userData.modelName, model.clone());
|
|
|
+
|
|
|
+ const idx = [];
|
|
|
+ if (model.userData.modelName === "SolarPowerPlant") {
|
|
|
+ model.traverse((ele) => {
|
|
|
+ if (ele.name.indexOf("SolarPanel") !== -1 || ele.name.indexOf("Solar_Panel") !== -1) {
|
|
|
+ ele.userData.modelName = "gfSolo";
|
|
|
+ ele.userData.isRootMaterialObject = true;
|
|
|
+ ele.userData.isRootModel = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else if (model.userData.modelName === "WindPowerSubstationPlant") {
|
|
|
+ model.traverse((ele) => {
|
|
|
+ if (ele.name.indexOf("WindTirbune") !== -1) {
|
|
|
+ idx.push(ele)
|
|
|
+ ele.userData.modelName = "fjSolo";
|
|
|
+ ele.userData.isRootMaterialObject = true;
|
|
|
+ ele.userData.isRootModel = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ this.addObject([model]);
|
|
|
+ BASE.closeLoading();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 和石化模型文件,为其添加材质根节点标识与名称汉化
|
|
|
+ this.formateModel = (modelInfo, modelFile) => {
|
|
|
+ if (modelInfo.userData.modelName === "gfSolo") {
|
|
|
+ modelFile.name = "太阳能逆变器"
|
|
|
+ const nameArray = ["太阳能电池板", "太阳能支架"];
|
|
|
+ modelFile.children.forEach((ele, index) => {
|
|
|
+ if (index < 2) {
|
|
|
+ ele.name = nameArray[index];
|
|
|
+ ele.userData.isRootMaterialObject = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else if (modelInfo.userData.modelName === "fjSolo") {
|
|
|
+ modelFile.name = "风力发电机"
|
|
|
+ const nameArray = ["风机塔筒", "风机叶片", "风机机舱"];
|
|
|
+ modelFile.children.forEach((ele, index) => {
|
|
|
+ if (index < 3) {
|
|
|
+ ele.name = nameArray[index];
|
|
|
+ ele.userData.isRootMaterialObject = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ if (modelFile.children?.length) {
|
|
|
+ modelFile.children.forEach((ele, index) => {
|
|
|
+ if (index < 3) {
|
|
|
+ ele.userData.isRootMaterialObject = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ modelFile.userData.isRootMaterialObject = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return modelFile;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取模型材质根节点
|
|
|
+ this.getRootMaterialObject = (model) => {
|
|
|
+ if (model.userData?.isRootMaterialObject || !model.parent) {
|
|
|
+ return model;
|
|
|
+ } else if (!model.userData?.isRootMaterialObject && model.parent) {
|
|
|
+ return this.getRootMaterialObject(model.parent);
|
|
|
+ } else {
|
|
|
+ return model;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取模型根节点
|
|
|
+ this.getRootModel = (model) => {
|
|
|
+ if (model.userData?.isRootModel || !model.parent) {
|
|
|
+ return model;
|
|
|
+ } else if (!model.userData?.isRootModel && model.parent) {
|
|
|
+ return this.getRootModel(model.parent);
|
|
|
+ } else {
|
|
|
+ return model;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建 3D 标签 DOM
|
|
|
+ this.createDom = () => {
|
|
|
+ let dom = document.createElement("div");
|
|
|
+ dom.style.padding = "5px 10px";
|
|
|
+ dom.style.border = "1px solid skyblue";
|
|
|
+ dom.style.borderRadius = '5px';
|
|
|
+ dom.style.height = '20px';
|
|
|
+ dom.style.pointerEvents = 'none';
|
|
|
+ return dom;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|