coordinateConverter.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import * as THREE from 'three'
  2. /**
  3. * 坐标系转换工具
  4. * 处理不同3D软件导出的模型坐标系差异
  5. */
  6. export type CoordinateSystem =
  7. | 'y-up' // Three.js 默认: Y轴朝上,Z轴向前
  8. | 'z-up' // Blender/3ds Max 默认: Z轴朝上,Y轴向前
  9. | 'z-up-forward-x' // 某些CAD软件: Z轴朝上,X轴向前
  10. /**
  11. * 转换模型坐标系
  12. * @param model - Three.js 3D对象
  13. * @param from - 源坐标系
  14. * @param to - 目标坐标系 (默认为 Three.js 的 y-up)
  15. * @param rotationOption - 旋转选项 (默认: standard, 可选: flipped, 180)
  16. * @returns 转换后的模型
  17. */
  18. export function convertCoordinateSystem(
  19. model: THREE.Object3D,
  20. from: CoordinateSystem,
  21. to: CoordinateSystem = 'y-up',
  22. rotationOption: 'standard' | 'flipped' | '180' = 'standard'
  23. ): THREE.Object3D {
  24. if (from === to) return model
  25. // 定义坐标系转换矩阵
  26. const conversionMatrices: Record<string, THREE.Matrix4> = {
  27. // Z-up (Blender) -> Y-up (Three.js)
  28. 'z-up_to_y-up_standard': new THREE.Matrix4().makeRotationX(-Math.PI / 2),
  29. 'z-up_to_y-up_flipped': new THREE.Matrix4().makeRotationX(Math.PI / 2),
  30. 'z-up_to_y-up_180': new THREE.Matrix4().makeRotationX(Math.PI),
  31. // Y-up (Three.js) -> Z-up (Blender)
  32. 'y-up_to_z-up_standard': new THREE.Matrix4().makeRotationX(Math.PI / 2),
  33. 'y-up_to_z-up_flipped': new THREE.Matrix4().makeRotationX(-Math.PI / 2),
  34. // Z-up-forward-x -> Y-up
  35. 'z-up-forward-x_to_y-up_standard': new THREE.Matrix4()
  36. .makeRotationX(-Math.PI / 2)
  37. .multiply(new THREE.Matrix4().makeRotationZ(Math.PI / 2)),
  38. }
  39. const key = `${from}_to_${to}_${rotationOption}`
  40. const matrix = conversionMatrices[key]
  41. if (!matrix) {
  42. console.warn(`不支持的坐标系转换: ${from} -> ${to} (${rotationOption})`)
  43. return model
  44. }
  45. // 应用转换矩阵
  46. model.applyMatrix4(matrix)
  47. return model
  48. }
  49. /**
  50. * 自动检测并转换模型坐标系
  51. * @param model - Three.js 3D对象
  52. * @returns 转换后的模型
  53. */
  54. export function autoConvertModelCoordinates(model: THREE.Object3D): THREE.Object3D {
  55. // 计算模型的包围盒
  56. const box = new THREE.Box3().setFromObject(model)
  57. const size = box.getSize(new THREE.Vector3())
  58. const center = box.getCenter(new THREE.Vector3())
  59. console.log(`[坐标系转换] 模型尺寸: (${size.x.toFixed(2)}, ${size.y.toFixed(2)}, ${size.z.toFixed(2)})`)
  60. console.log(`[坐标系转换] 包围盒中心: (${center.x.toFixed(2)}, ${center.y.toFixed(2)}, ${center.z.toFixed(2)})`)
  61. // 判断坐标系:如果 Z 轴尺寸最大且 Y 轴较小,可能是 Z-up 坐标系
  62. const isZUp = size.z > size.y && size.z > size.x
  63. if (isZUp) {
  64. console.log(`[坐标系转换] 检测到 Z-up 坐标系,转换为 Y-up`)
  65. return convertCoordinateSystem(model, 'z-up', 'y-up')
  66. } else {
  67. console.log(`[坐标系转换] 检测到 Y-up 坐标系,无需转换`)
  68. return model
  69. }
  70. }
  71. /**
  72. * 创建坐标系参考轴
  73. * @param size - 轴的长度
  74. * @returns 坐标系参考轴对象
  75. */
  76. export function createCoordinateAxes(size: number = 10): THREE.Group {
  77. const group = new THREE.Group()
  78. // X轴 (红色)
  79. const xAxis = new THREE.ArrowHelper(
  80. new THREE.Vector3(1, 0, 0),
  81. new THREE.Vector3(0, 0, 0),
  82. size,
  83. 0xff0000,
  84. size * 0.2,
  85. size * 0.1
  86. )
  87. // Y轴 (绿色)
  88. const yAxis = new THREE.ArrowHelper(
  89. new THREE.Vector3(0, 1, 0),
  90. new THREE.Vector3(0, 0, 0),
  91. size,
  92. 0x00ff00,
  93. size * 0.2,
  94. size * 0.1
  95. )
  96. // Z轴 (蓝色)
  97. const zAxis = new THREE.ArrowHelper(
  98. new THREE.Vector3(0, 0, 1),
  99. new THREE.Vector3(0, 0, 0),
  100. size,
  101. 0x0000ff,
  102. size * 0.2,
  103. size * 0.1
  104. )
  105. group.add(xAxis)
  106. group.add(yAxis)
  107. group.add(zAxis)
  108. return group
  109. }
  110. /**
  111. * 坐标系转换配置接口
  112. */
  113. export interface CoordinateConversionConfig {
  114. sourceSystem: CoordinateSystem
  115. targetSystem?: CoordinateSystem
  116. autoDetect?: boolean
  117. showAxes?: boolean
  118. axesSize?: number
  119. }
  120. /**
  121. * 应用坐标系转换到模型组
  122. */
  123. export function applyCoordinateConversion(
  124. modelGroup: THREE.Group,
  125. config: CoordinateConversionConfig
  126. ): THREE.Group {
  127. const {
  128. sourceSystem,
  129. targetSystem = 'y-up',
  130. autoDetect = false,
  131. showAxes = false,
  132. axesSize = 10
  133. } = config
  134. let convertedModel: THREE.Object3D
  135. if (autoDetect) {
  136. convertedModel = autoConvertModelCoordinates(modelGroup)
  137. } else {
  138. convertedModel = convertCoordinateSystem(modelGroup, sourceSystem, targetSystem)
  139. }
  140. // 创建新的组来包含转换后的模型
  141. const resultGroup = new THREE.Group()
  142. if (convertedModel instanceof THREE.Group) {
  143. // 如果转换后还是组,直接添加所有子对象
  144. convertedModel.children.forEach(child => {
  145. resultGroup.add(child.clone())
  146. })
  147. } else {
  148. resultGroup.add(convertedModel)
  149. }
  150. // 可选:添加坐标系参考轴
  151. if (showAxes) {
  152. const axes = createCoordinateAxes(axesSize)
  153. resultGroup.add(axes)
  154. }
  155. return resultGroup
  156. }