fanLocal.threejs.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import * as THREE from 'three';
  2. import UseThree from '../../../../utils/threejs/useThree';
  3. import { animateCamera } from '/@/utils/threejs/util';
  4. import useEvent from '../../../../utils/threejs/useEvent';
  5. import { getDictItemsByCode } from '/@/utils/dict';
  6. /** 模型总控制器 */
  7. let model: UseThree;
  8. /** 当前展示的具体模型的 Object3D 对象 */
  9. let group: THREE.Object3D;
  10. /** 当前展示的具体模型及状态组成的判断唯一性的key */
  11. let cacheKey: string = '';
  12. /** 具体模型内容列表,包含此模型总控制器下的所有可用的具体模型内容 */
  13. const modelContextList: {
  14. /** 当前模型类型,在控制器下有多个具体模型时分辨它们 */
  15. type: string;
  16. /** 模型的具体内容,即负责模型导入、绘制的上下文对象,一个控制器可以新建多个 */
  17. context?: FanLocal | FanLocalDual | FanLocalTwo;
  18. }[] = [];
  19. const { mouseDownFn } = useEvent();
  20. /** 分发鼠标事件到具体实现方法 */
  21. function dispatchMouseEvent(event) {
  22. if (event.button == 0 && model && group) {
  23. mouseDownFn(model, group, event, () => {});
  24. console.log(model.camera, model.orbitControls);
  25. }
  26. }
  27. /** 为模型控制器设置非默认的摄像头位置 */
  28. function setCamera() {
  29. // if (!model || !model.camera) return;
  30. // model.camera.position.set(0, -2000, 1000);
  31. // model.camera.far = 10000;
  32. // model.orbitControls?.update();
  33. // model.camera.updateProjectionMatrix();
  34. }
  35. /** 初始化模型CSS展示框的鼠标事件,应该在模型总控制器初始化后调用 */
  36. function initEventListender() {
  37. if (!model) return;
  38. model.canvasContainer?.addEventListener('mousedown', (e) => dispatchMouseEvent(e));
  39. // model.orbitControls?.addEventListener('change', () => render());
  40. }
  41. /** 渲染并更新总模型 */
  42. // function render() {
  43. // if (model && model.isRender && model.renderer) {
  44. // // model.animationId = requestAnimationFrame(render);
  45. // model.css3dRender?.render(model.scene as THREE.Scene, model.camera as THREE.PerspectiveCamera);
  46. // model.renderer.render(model.scene as THREE.Scene, model.camera as THREE.PerspectiveCamera);
  47. // model.stats?.update();
  48. // }
  49. // }
  50. /** 刷新(再渲染)总模型 */
  51. // export function refreshModal() {
  52. // render();
  53. // // modelContextList.forEach((item) => {
  54. // // if (item.context) {
  55. // // item.context.render();
  56. // // }
  57. // // });
  58. // }
  59. /** 设置模型类型并切换,不同的类型通常对应不同的具体模型,在模型总控制器下的具体模型会根据传入的参数彼此交互、切换 */
  60. export function setModelType(modelType: 'fanLocal' | 'fanLocalDual' | 'fanLocalSingle' | string, subModelType: string, data?: any) {
  61. return new Promise((resolve, reject) => {
  62. if (!model) return reject('模型控制器未初始化');
  63. // 判断是否是同一个/类模型
  64. if (cacheKey === `${modelType}-${subModelType}`) return resolve(null);
  65. // const isUpdate = cacheKey.startsWith(modelType);
  66. cacheKey = `${modelType}-${subModelType}`;
  67. modelContextList.forEach(({ type, context }) => {
  68. if (!context || !context.group) return;
  69. // 先把模型相关的内容隐藏,另起的隐藏子元素的代码是为了隐藏 CSS 元素
  70. context.group.visible = false;
  71. context.group.children.forEach((e) => {
  72. e.visible = false;
  73. });
  74. // model.scene?.remove(context.group);
  75. if (modelType === type) {
  76. group = context?.group as THREE.Object3D;
  77. if (context.setModelType) context.setModelType(subModelType, data);
  78. // 还没添加到控制器的添加进去
  79. if (!model.scene?.getObjectByName(group.name) && group) {
  80. model.scene?.add(group);
  81. }
  82. group.visible = true;
  83. group.children.forEach((e) => {
  84. e.visible = true;
  85. });
  86. // 模型发生了替换,需要使用摄像头动画// 模型不同需要不同的初始角度与位置
  87. if (type == 'fanLocal') {
  88. const oldCameraPosition = { x: 615, y: 275, z: 744 };
  89. animateCamera(oldCameraPosition, { x: 0, y: 0, z: 0 }, { x: -1.85, y: 13.58, z: 37.39 }, { x: -1.83, y: 2.58, z: -0.75 }, model, 0.8);
  90. }
  91. if (type == 'fanLocalTwo') {
  92. const oldCameraPosition = { x: 615, y: 275, z: 744 };
  93. animateCamera(oldCameraPosition, { x: 0, y: 0, z: 0 }, { x: -1.85, y: 13.58, z: 37.39 }, { x: -1.83, y: 2.58, z: -0.75 }, model, 0.8);
  94. }
  95. if (type == 'fanLocalDual') {
  96. const oldCameraPosition = { x: -693, y: 474, z: 398 };
  97. animateCamera(oldCameraPosition, { x: 0, y: 0, z: 0 }, { x: 14.83, y: 16.9, z: 36.46 }, { x: 0, y: 0, z: 0 }, model, 0.8);
  98. }
  99. resolve(null);
  100. }
  101. });
  102. });
  103. }
  104. /** 挂载模型控制器,sceneSelctor表示放置模型的元素选择器,cssSelectors表示放置类似详情框的元素选择器,其中第一项需要是根元素选择器 */
  105. export function mountedThree(sceneSelctor: string, cssSelectors: string[]) {
  106. return new Promise(async (resolve) => {
  107. const [rootSelector] = cssSelectors;
  108. model = new UseThree(sceneSelctor, rootSelector);
  109. model.setEnvMap('test1.hdr');
  110. /** @ts-ignore-next-line */
  111. model.renderer.toneMappingExposure = 1.0;
  112. if (model.renderer) {
  113. model.renderer.sortObjects = true;
  114. }
  115. // 这里根据字典判断
  116. const dictCodes = getDictItemsByCode('fanlocal_install_kind');
  117. if (dictCodes && dictCodes.length > 0) {
  118. for (let i = 0; i < dictCodes.length; i++) {
  119. const dict = dictCodes[i];
  120. switch (dict.value) {
  121. case 'single':
  122. const FanLocal = await import('./fanLocal.threejs.base');
  123. const model1 = new FanLocal.default(model);
  124. await model1.mountedThree();
  125. modelContextList.push({
  126. type: 'fanLocal',
  127. context: model1,
  128. });
  129. break;
  130. case 'dual_inner':
  131. case 'dual_outer':
  132. if (modelContextList.find((item) => item.type == 'fanLocalDual')) continue;
  133. const FanLocalDual = await import('./fanLocalDual.threejs.base');
  134. const model2 = new FanLocalDual.default(model);
  135. await model2.mountedThree();
  136. // 暂时先不加双行
  137. modelContextList.push({
  138. type: 'fanLocalDual',
  139. context: model2,
  140. });
  141. break;
  142. case 'fanLocalTwo':
  143. const FanLocalTwo = await import('./fanLocal.threejs.Two');
  144. const model3 = new FanLocalTwo.default(model);
  145. const flag = await model3.mountedThree();
  146. if (flag)
  147. modelContextList.push({
  148. type: 'fanLocalTwo',
  149. context: model3,
  150. });
  151. break;
  152. case 'fanlocal_1':
  153. const FanLocalSingle = await import('./fanLocal.threejs.single');
  154. const model4 = new FanLocalSingle.default(model);
  155. await model4.mountedThree();
  156. modelContextList.push({
  157. type: 'fanLocalSingle',
  158. context: model4,
  159. });
  160. break;
  161. }
  162. }
  163. }
  164. initEventListender();
  165. setCamera();
  166. model.animate();
  167. resolve(null);
  168. });
  169. }
  170. export const destroy = () => {
  171. if (!model) return;
  172. model.isRender = false;
  173. modelContextList.forEach((item) => {
  174. if (item.context) item.context.destroy();
  175. });
  176. model.destroy();
  177. };
  178. // 为了兼容性而添加的方法导出
  179. export function addText(d, e) {
  180. if (modelContextList[0]) modelContextList[0].context?.addText(d);
  181. if (modelContextList[1]) modelContextList[1].context?.addText(e);
  182. if (modelContextList[2]) modelContextList[2].context?.addText(d);
  183. }
  184. export function addCssText() {
  185. if (modelContextList[0] && modelContextList[0].context && modelContextList[0].context['addCssText']) modelContextList[0].context['addCssText']();
  186. if (modelContextList[2] && modelContextList[2].context && modelContextList[2].context['addCssText']) modelContextList[2].context['addCssText']();
  187. }
  188. export function playSmoke(d) {
  189. for (let i = 0; i < modelContextList.length; i++) {
  190. const item = modelContextList[i];
  191. if (item.context && item.context.playSmoke) item.context.playSmoke(d);
  192. }
  193. }