|
@@ -0,0 +1,283 @@
|
|
|
+import * as THREE from 'three'; //导入整个 three.js核心库
|
|
|
+import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
|
|
|
+import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
|
|
|
+import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
|
|
|
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; //导入控制器模块,轨道控制器
|
|
|
+import { CSS3DSprite } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
|
|
|
+import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
|
|
|
+import { getBottomMaterial } from './material.js';
|
|
|
+// 定义一个 class类
|
|
|
+class renderModel {
|
|
|
+ constructor(selector) {
|
|
|
+ this.container = document.querySelector(selector);
|
|
|
+ // 相机
|
|
|
+ this.camera;
|
|
|
+ // 场景
|
|
|
+ this.scene;
|
|
|
+ //渲染器
|
|
|
+ this.renderer;
|
|
|
+ // 控制器
|
|
|
+ this.controls;
|
|
|
+ // 3d文字渲染器
|
|
|
+ this.css3DRenderer = null;
|
|
|
+ // 3d文字控制器
|
|
|
+ this.css3dControls = null;
|
|
|
+ // 模型
|
|
|
+ this.model;
|
|
|
+ // 环境光
|
|
|
+ this.ambientLight;
|
|
|
+ //模型平面
|
|
|
+ this.planeGeometry;
|
|
|
+ }
|
|
|
+ // 初始化加载模型方法
|
|
|
+ init() {
|
|
|
+ //初始化场景
|
|
|
+ this.initScene();
|
|
|
+ //初始化相机
|
|
|
+ this.initCamera();
|
|
|
+ //初始化渲染器
|
|
|
+ this.initRender();
|
|
|
+ // 创建灯光
|
|
|
+ this.createLight();
|
|
|
+ //初始化控制器,控制摄像头,控制器一定要在渲染器后
|
|
|
+ this.initControls();
|
|
|
+ this.setFBXModel();
|
|
|
+ //监听场景大小改变,跳转渲染尺寸
|
|
|
+ window.addEventListener('resize', this.onWindowResizes);
|
|
|
+ //场景渲染
|
|
|
+ this.sceneAnimation();
|
|
|
+ }
|
|
|
+ //创建场景
|
|
|
+ initScene() {
|
|
|
+ this.scene = new THREE.Scene();
|
|
|
+ this.scene.background = new THREE.Color(0x000000);
|
|
|
+ const axesHelper = new THREE.AxesHelper(150);
|
|
|
+ this.scene.add(axesHelper);
|
|
|
+ //底部平面
|
|
|
+ const geometry = new THREE.PlaneGeometry(1, 1);
|
|
|
+ // 创建材质,可以设置贴图
|
|
|
+ const material = getBottomMaterial(
|
|
|
+ '#ffffff', // 设置模型的颜色
|
|
|
+ require('@/assets/images/models/bg.png'),
|
|
|
+ 1,
|
|
|
+ 5
|
|
|
+ );
|
|
|
+ // 创建平面网格
|
|
|
+ const plane = new THREE.Mesh(geometry, material);
|
|
|
+ plane.scale.set(1000, 1000, 1000);
|
|
|
+ plane.position.y = 0;
|
|
|
+ const plane2 = plane.clone();
|
|
|
+ plane2.material = getBottomMaterial(
|
|
|
+ '#000000', // 设置模型的颜色
|
|
|
+ require('@/assets/images/models/bg.png'),
|
|
|
+ 2,
|
|
|
+ 5
|
|
|
+ );
|
|
|
+ this.scene.add(plane2);
|
|
|
+ this.scene.add(plane);
|
|
|
+ }
|
|
|
+ // 创建相机
|
|
|
+ initCamera() {
|
|
|
+ const { clientHeight, clientWidth } = this.container;
|
|
|
+ this.camera = new THREE.PerspectiveCamera(14, clientWidth / clientHeight, 1, 10000);
|
|
|
+ }
|
|
|
+ // 创建渲染器
|
|
|
+ initRender() {
|
|
|
+ // this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) //设置抗锯齿
|
|
|
+ this.renderer = new THREE.WebGLRenderer({
|
|
|
+ logarithmicDepthBuffer: true,
|
|
|
+ antialias: true, // true/false表示是否开启反锯齿
|
|
|
+ alpha: true, // true/false 表示是否可以设置背景色透明
|
|
|
+ precision: 'mediump', // highp/mediump/lowp 表示着色精度选择
|
|
|
+ premultipliedAlpha: true // true/false 表示是否可以设置像素深度(用来度量图像的分辨率)
|
|
|
+ // preserveDrawingBuffer: false, // true/false 表示是否保存绘图缓冲
|
|
|
+ // physicallyCorrectLights: true, // true/false 表示是否开启物理光照
|
|
|
+ });
|
|
|
+ //设置屏幕像素比
|
|
|
+ this.renderer.setPixelRatio(window.devicePixelRatio);
|
|
|
+ //渲染的尺寸大小
|
|
|
+ const { clientHeight, clientWidth } = this.container;
|
|
|
+ this.renderer.setSize(clientWidth, clientHeight);
|
|
|
+ this.container.appendChild(this.renderer.domElement);
|
|
|
+
|
|
|
+ // 创建一个CSS3DRenderer
|
|
|
+ // this.css3DRenderer = new CSS3DRenderer()
|
|
|
+ // this.css3DRenderer.setSize(clientWidth, clientHeight)
|
|
|
+ // this.css3DRenderer.domElement.style.position = 'absolute'
|
|
|
+ // this.css3DRenderer.domElement.style.pointerEvents = 'none'
|
|
|
+ // this.css3DRenderer.domElement.style.top = 0
|
|
|
+ }
|
|
|
+ // 创建光源
|
|
|
+ createLight() {
|
|
|
+ this.scene.add(new THREE.AmbientLight(0xffffff, 10));
|
|
|
+ }
|
|
|
+ initControls() {
|
|
|
+ this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
|
+ this.controls.enableDamping = true;
|
|
|
+ this.controls.maxDistance = 1000;
|
|
|
+ this.controls.update();
|
|
|
+ //标签控制器
|
|
|
+ // this.css3dControls = new OrbitControls(this.camera, this.css3DRenderer.domElement)
|
|
|
+ // this.css3dControls.enablePan = false
|
|
|
+ // this.css3dControls.enableDamping = true
|
|
|
+ // this.css3dControls.target.set(0, 0, 0)
|
|
|
+ // this.css3dControls.update()
|
|
|
+ }
|
|
|
+ animate() {
|
|
|
+ this.renderer.render(this.scene, this.camera);
|
|
|
+ this.controls.update();
|
|
|
+ }
|
|
|
+ // 使用动画器不断更新场景
|
|
|
+ sceneAnimation() {
|
|
|
+ this.renderer.setAnimationLoop(this.animate.bind(this));
|
|
|
+ }
|
|
|
+ tag3D(name) {
|
|
|
+ // 创建div元素(作为标签)
|
|
|
+ let div = document.createElement('div');
|
|
|
+ div.innerHTML = name;
|
|
|
+ div.style.color = '#000';
|
|
|
+ // div.classList.add("tag");
|
|
|
+ //div元素包装为CSS3模型对象CSS3DObject
|
|
|
+ let label = new CSS3DSprite(div);
|
|
|
+ div.style.pointerEvents = 'none'; //避免HTML标签遮挡三维场景的鼠标事件
|
|
|
+ // 设置HTML元素标签在three.js世界坐标中位置
|
|
|
+ // label.position.set(x, y, z);
|
|
|
+ //缩放CSS3DObject模型对象
|
|
|
+ // label.scale.set(0.02, 0.02, 0.02); //根据相机渲染范围控制HTML 3D标签尺寸
|
|
|
+ // label.rotateY(Math.PI / 2); //控制HTML标签CSS3对象姿态角度
|
|
|
+ // label.rotateX(-Math.PI/2);
|
|
|
+ return label; //返回CSS3模型标签
|
|
|
+ }
|
|
|
+
|
|
|
+ dialog(html) {
|
|
|
+ //div元素包装为CSS3模型对象CSS3DSprite
|
|
|
+ const element = document.createElement('div');
|
|
|
+ element.className = 'tag';
|
|
|
+ element.innerHTML = html;
|
|
|
+ const dialog = new CSS3DSprite(element);
|
|
|
+ element.style.pointerEvents = 'none'; //避免HTML标签遮挡三维场景的鼠标事件
|
|
|
+ // 设置HTML元素标签在three.js世界坐标中位置
|
|
|
+ // label.position.set(x, y, z);
|
|
|
+ //缩放CSS3DSprite模型对象
|
|
|
+ dialog.scale.set(0.01, 0.01, 0.01); //根据相机渲染范围控制HTML 3D标签尺寸
|
|
|
+ dialog.rotateY(Math.PI / 2); //控制HTML标签CSS3对象姿态角度
|
|
|
+ // dialog.rotateX(-Math.PI/2);
|
|
|
+ return dialog; //返回CSS3模型标签
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 设置加载模型居中
|
|
|
+ * {Object} object 模型对象
|
|
|
+ */
|
|
|
+ setModelPosition(object) {
|
|
|
+ object.updateMatrixWorld();
|
|
|
+ // 获得包围盒得min和max
|
|
|
+ const box = new THREE.Box3().setFromObject(object);
|
|
|
+ // 返回包围盒的中心点
|
|
|
+ const center = box.getCenter(new THREE.Vector3());
|
|
|
+ object.position.x += object.position.x - center.x;
|
|
|
+ object.position.y += object.position.y - center.y;
|
|
|
+ object.position.z = 0;
|
|
|
+ }
|
|
|
+ //加载模型
|
|
|
+ setBaseModel() {
|
|
|
+ var mtlLoader = new MTLLoader();
|
|
|
+ mtlLoader.load('model/model.mtl', materials => {
|
|
|
+ var objLoader = new OBJLoader();
|
|
|
+ objLoader.setMaterials(materials);
|
|
|
+ //加载.obj文件,执行obj函数
|
|
|
+ objLoader.load('model/model3.obj', object => {
|
|
|
+ this.setModelPosition(object);
|
|
|
+ this.model = object;
|
|
|
+ // object.traverse((obj) => {
|
|
|
+ // if (obj instanceof THREE.Mesh) {
|
|
|
+ // obj.material = new THREE.MeshPhysicalMaterial({ color: '#43617B' })
|
|
|
+ // }
|
|
|
+ // })
|
|
|
+ object.traverse(function (child) {
|
|
|
+ if (child.isMesh) {
|
|
|
+ child.material.emissive = child.material.color;
|
|
|
+ child.material.emissiveMap = child.material.map;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 设置相机位置
|
|
|
+ this.camera.position.set(0, -600, 200);
|
|
|
+ // 设置相机坐标系
|
|
|
+ this.camera.lookAt(0, 0, 0);
|
|
|
+ // 将模型添加到场景中去
|
|
|
+ this.scene.add(this.model);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ setFBXModel() {
|
|
|
+ var fbxLoader = new FBXLoader();
|
|
|
+ //加载.obj文件,执行obj函数
|
|
|
+ fbxLoader.load('/models/model.FBX', object => {
|
|
|
+ this.calcMeshCenter(object);
|
|
|
+ this.model = object;
|
|
|
+ object.traverse(function (child) {
|
|
|
+ if (child.isMesh) {
|
|
|
+ child.material.emissive = child.material.color;
|
|
|
+ child.material.emissiveMap = child.material.map;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 设置相机位置
|
|
|
+ this.camera.position.set(0, -800, 500);
|
|
|
+ // 设置相机坐标系
|
|
|
+ this.camera.lookAt(0, 0, 0);
|
|
|
+ // 将模型添加到场景中去
|
|
|
+ this.scene.add(this.model);
|
|
|
+ // 初始化 GUI 控件
|
|
|
+ const camFolder = new GUI().addFolder('Camera');
|
|
|
+ camFolder.add(this.camera.position, 'x', -6500, 6500, 10).name('X Axis');
|
|
|
+ camFolder.add(this.camera.position, 'y', -6500, 6500, 10).name('Y Axis');
|
|
|
+ camFolder.add(this.camera.position, 'z', -6500, 6500, 10).name('Z Axis');
|
|
|
+ camFolder.open();
|
|
|
+ // const div = document.createElement('div');
|
|
|
+ // div.className = 'workshop-text';
|
|
|
+ // div.innerHTML = '<p>仓库2</p>';
|
|
|
+ // // 创建CSS3DSprite
|
|
|
+ // const tag = new CSS3DSprite(div);
|
|
|
+ // tag.position.set(8, 3, 38); // 调整标签位置
|
|
|
+ // this.model.add(tag);
|
|
|
+ //在GUI中添加一个用于调整tag位置的文件夹
|
|
|
+ // const tagFolder = new GUI().addFolder('食堂标签');
|
|
|
+ // // 将tag.position对象中的x, y, z属性添加到GUI中
|
|
|
+ // tagFolder.add(tag.position, 'x', -200, 200).name('X Position');
|
|
|
+ // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position');
|
|
|
+ // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position');
|
|
|
+ // tagFolder.open(); // 打开此文件夹以默认显示控制器
|
|
|
+ // const label3D = this.tag3D('光伏11111111'); //设置标签名称
|
|
|
+ // // label3D.position.copy(this.model.position);
|
|
|
+ // label3D.position.y += 0;
|
|
|
+ // label3D.position.x += 0;
|
|
|
+ // label3D.position.z = 20;
|
|
|
+ // this.model.add(label3D);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ onWindowResizes() {
|
|
|
+ if (!this.container) return false;
|
|
|
+ const { clientHeight, clientWidth } = this.container;
|
|
|
+ //调整屏幕大小
|
|
|
+ this.camera.aspect = clientWidth / clientHeight; // 摄像机宽高比例
|
|
|
+ this.camera.updateProjectionMatrix(); //相机更新矩阵,将3d内容投射到2d面上转换
|
|
|
+ this.renderer.setSize(clientWidth, clientHeight);
|
|
|
+ }
|
|
|
+ calcMeshCenter(group) {
|
|
|
+ /**
|
|
|
+ * 包围盒全自动计算:模型整体居中
|
|
|
+ */
|
|
|
+ var box3 = new THREE.Box3();
|
|
|
+ // 计算层级模型group的包围盒
|
|
|
+ // 模型group是加载一个三维模型返回的对象,包含多个网格模型
|
|
|
+ box3.expandByObject(group);
|
|
|
+ // 计算一个层级模型对应包围盒的几何体中心在世界坐标中的位置
|
|
|
+ var center = new THREE.Vector3();
|
|
|
+ box3.getCenter(center);
|
|
|
+ // 重新设置模型的位置,使之居中。
|
|
|
+ group.position.x = 0;
|
|
|
+ group.position.y = 0;
|
|
|
+ group.position.z = 17;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export default renderModel;
|