Skip to content

Three.js相关

Three.js 方法封装

TIP

本项目中 Three.js 的 API 相关的操作方法都统一抽离封装在 utils/renderScene 和 utils/sceneModules 文件夹中。

初始化项目的 Three.js 场景实例

js
<template>
  <div id="scene-render"></div>
</template>;
import renderScene from "@/utils/renderScene";
import { onMounted } from "vue";
onMounted(async () => {
  const renderScene = new renderScene("#scene-render");
  // 初始化场景内容
  await renderScene.init();
  // 加载进度条
  renderScene.onProgress((progressNum: number, totalSize: number) => {
    console.log(progressNum);
  });
});

注 ⚠️:完整的代码可查看 views/sceneEdit/index.vue 文件

全局使用 renderScene API

将 new 实例化出来的 renderScene 内容定义在 Pinia 全局 store 中实现多页面共享

js
<template>
  <div id="scene-render"></div>
</template>;
import renderScene from "@/utils/renderScene";
import { useModelStore } from "@/store/sceneEditStore";
import { onMounted } from "vue";

const store = useModelStore();

onMounted(async () => {
  const renderScene = new renderScene("#scene-render");
  store.setSceneApi(renderScene);
  // 初始化场景内容
  await renderScene.init();
  // 加载进度条
  renderScene.onProgress((progressNum: number, totalSize: number) => {
    console.log(progressNum);
  });
});

引入 useModelStore 在其他页面中使用

js
import { useModelStore } from "@/store/sceneEditStore";

const store = useModelStore();

// 选择材质
const changeMaterialsNode = (node: MaterialNode) => {
  store.setCurrentTransformMaterialUuid(node.uuid);
  store.sceneApi?.chooseMaterial(node);
};

// 复制材质
const copyMaterial = (node: MaterialNode) => {
  store.sceneApi?.copySceneMaterial(node.uuid);
};

模块化开发

注意 ⚠️

Three.js 相关方法的操作本身就是相当繁琐且复杂的,一个功能的实现可能就需要几十行甚至上百行的代码,在企业级项目 3D 开发中如果你的代码设计不太合理,可能会导致一个文件出现几千行的代码情况,这对于项目后期的维护和扩展将会是灾难性的。

为了项目后期的扩展和可维护性,项目根据实际的功能模块将 Three.js 相关方法分为了几个大的模块分别用于实现不同的功能

@/utils/renderScene.ts 是 Three.js 方法内容的主文件,包含了场景、相机、渲染器、控制器、地面、第一人称、模型加载等方法。

对于灯光、动画、天气、变换控制器、历史操作记录等模块内容我们也进行了单独的封装定义

如果你在实现项目开发过程中也会有新的模块需求拆分,可借鉴这种模式

js
import AnimationModules from "./sceneModules/animationModules";
import TransformControlsModules from "./sceneModules/transformControlsModules";
import LightModules from "./sceneModules/lightModules";
import WeatherEffectsModules from "./sceneModules/weatherEffectsModules";
import css3DRendererModules from "./sceneModules/css3DRendererModules";

class renderScene {
  // 动画模块实例
  animationModules: {
    playAnimation: (animation: THREE.AnimationClip, model: THREE.Group) => void,
    updateAnimationParams: (action: ActionParams, uuid: string) => void,
    updateActionAnimationMap: (mapId: string, uuid: string) => void,
    currentActions: Map<string, THREE.AnimationAction[]>,
    clear: () => void,
  };
  // 光源模块实例
  lightModules: {
    createLight: (type: string, position: THREE.Vector3) => void,
    updateHelper: (uuid?: string) => void,
    initLight: () => void,
  };
  // 变换控制器模块实例
  transformControlsModules: {
    init: () => void,
    transformControls: TransformControls | null,
    transformControlsHelper: THREE.Object3D | null,
    focusOnObject: (object: THREE.Object3D) => void,
    destroy: () => void,
    createTransformControls: () => void,
    clearCurrentSelection: () => void,
  };
  // 天气效果模块实例
  weatherEffectsModules: {
    createWeatherEffect: (options: WeatherOptions) => void,
    updateWeatherParams: (options: WeatherOptions) => void,
    weatherConfig: WeatherOptions,
  };
  // 历史记录模块实例
  historyModules: {
    undo: () => void,
    redo: () => void,
    clear: () => void,
    execute: (command: Command) => void,
  };
  //css3D实例
  css3DRendererModules: {
    init: (clientWidth: number, clientHeight: number) => void,
    render: (scene: THREE.Scene, camera: THREE.PerspectiveCamera) => void,
    css3DRenderer: CSS3DRenderer | null,
    createEcharts: (options: unknown) => void,
    initEcharts: () => void,
  };
  constructor(selector: string) {
    this.animationModules = new AnimationModules();
    this.lightModules = new LightModules();
    this.transformControlsModules = new TransformControlsModules();
    this.weatherEffectsModules = new WeatherEffectsModules();
    this.historyModules = new HistoryModules();
    this.css3DRendererModules = new css3DRendererModules();
  }
}
export default renderScene;

在任何一个页面中去使用

js
import { useModelStore } from '@/store/sceneEditStore';
const store = useModelStore();

// 变换控制器类型切换
const handleTransformControlsType = (type: TRANSFORM_CONTROLS_TYPE) => {
  store.sceneApi?.transformControlsModules.transformControls?.setMode(type);
};

// 键盘 f 键 镜头聚焦模型位置
const keyDownEventListener =(event: KeyboardEvent)=>{
  const { currentTransformMaterialUuid , sceneApi } = store
  if (event.key.toLowerCase() === 'f') {
    event.preventDefault();
    const mesh = store.sceneApi?.scene?.getObjectByProperty('uuid',currentTransformMaterialUuid
    );
    if (mesh) {
      sceneApi?.transformControlsModules.focusOnObject(mesh);
    }
  }
}

本文档内容版权属于threejs-3dmodel-edit作者,保留所有权利