import * as THREE from 'three' /** * 坐标系转换工具 * 处理不同3D软件导出的模型坐标系差异 */ export type CoordinateSystem = | 'y-up' // Three.js 默认: Y轴朝上,Z轴向前 | 'z-up' // Blender/3ds Max 默认: Z轴朝上,Y轴向前 | 'z-up-forward-x' // 某些CAD软件: Z轴朝上,X轴向前 /** * 转换模型坐标系 * @param model - Three.js 3D对象 * @param from - 源坐标系 * @param to - 目标坐标系 (默认为 Three.js 的 y-up) * @param rotationOption - 旋转选项 (默认: standard, 可选: flipped, 180) * @returns 转换后的模型 */ export function convertCoordinateSystem( model: THREE.Object3D, from: CoordinateSystem, to: CoordinateSystem = 'y-up', rotationOption: 'standard' | 'flipped' | '180' = 'standard' ): THREE.Object3D { if (from === to) return model // 定义坐标系转换矩阵 const conversionMatrices: Record = { // Z-up (Blender) -> Y-up (Three.js) 'z-up_to_y-up_standard': new THREE.Matrix4().makeRotationX(-Math.PI / 2), 'z-up_to_y-up_flipped': new THREE.Matrix4().makeRotationX(Math.PI / 2), 'z-up_to_y-up_180': new THREE.Matrix4().makeRotationX(Math.PI), // Y-up (Three.js) -> Z-up (Blender) 'y-up_to_z-up_standard': new THREE.Matrix4().makeRotationX(Math.PI / 2), 'y-up_to_z-up_flipped': new THREE.Matrix4().makeRotationX(-Math.PI / 2), // Z-up-forward-x -> Y-up 'z-up-forward-x_to_y-up_standard': new THREE.Matrix4() .makeRotationX(-Math.PI / 2) .multiply(new THREE.Matrix4().makeRotationZ(Math.PI / 2)), } const key = `${from}_to_${to}_${rotationOption}` const matrix = conversionMatrices[key] if (!matrix) { console.warn(`不支持的坐标系转换: ${from} -> ${to} (${rotationOption})`) return model } // 应用转换矩阵 model.applyMatrix4(matrix) return model } /** * 自动检测并转换模型坐标系 * @param model - Three.js 3D对象 * @returns 转换后的模型 */ export function autoConvertModelCoordinates(model: THREE.Object3D): THREE.Object3D { // 计算模型的包围盒 const box = new THREE.Box3().setFromObject(model) const size = box.getSize(new THREE.Vector3()) const center = box.getCenter(new THREE.Vector3()) console.log(`[坐标系转换] 模型尺寸: (${size.x.toFixed(2)}, ${size.y.toFixed(2)}, ${size.z.toFixed(2)})`) console.log(`[坐标系转换] 包围盒中心: (${center.x.toFixed(2)}, ${center.y.toFixed(2)}, ${center.z.toFixed(2)})`) // 判断坐标系:如果 Z 轴尺寸最大且 Y 轴较小,可能是 Z-up 坐标系 const isZUp = size.z > size.y && size.z > size.x if (isZUp) { console.log(`[坐标系转换] 检测到 Z-up 坐标系,转换为 Y-up`) return convertCoordinateSystem(model, 'z-up', 'y-up') } else { console.log(`[坐标系转换] 检测到 Y-up 坐标系,无需转换`) return model } } /** * 创建坐标系参考轴 * @param size - 轴的长度 * @returns 坐标系参考轴对象 */ export function createCoordinateAxes(size: number = 10): THREE.Group { const group = new THREE.Group() // X轴 (红色) const xAxis = new THREE.ArrowHelper( new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 0, 0), size, 0xff0000, size * 0.2, size * 0.1 ) // Y轴 (绿色) const yAxis = new THREE.ArrowHelper( new THREE.Vector3(0, 1, 0), new THREE.Vector3(0, 0, 0), size, 0x00ff00, size * 0.2, size * 0.1 ) // Z轴 (蓝色) const zAxis = new THREE.ArrowHelper( new THREE.Vector3(0, 0, 1), new THREE.Vector3(0, 0, 0), size, 0x0000ff, size * 0.2, size * 0.1 ) group.add(xAxis) group.add(yAxis) group.add(zAxis) return group } /** * 坐标系转换配置接口 */ export interface CoordinateConversionConfig { sourceSystem: CoordinateSystem targetSystem?: CoordinateSystem autoDetect?: boolean showAxes?: boolean axesSize?: number } /** * 应用坐标系转换到模型组 */ export function applyCoordinateConversion( modelGroup: THREE.Group, config: CoordinateConversionConfig ): THREE.Group { const { sourceSystem, targetSystem = 'y-up', autoDetect = false, showAxes = false, axesSize = 10 } = config let convertedModel: THREE.Object3D if (autoDetect) { convertedModel = autoConvertModelCoordinates(modelGroup) } else { convertedModel = convertCoordinateSystem(modelGroup, sourceSystem, targetSystem) } // 创建新的组来包含转换后的模型 const resultGroup = new THREE.Group() if (convertedModel instanceof THREE.Group) { // 如果转换后还是组,直接添加所有子对象 convertedModel.children.forEach(child => { resultGroup.add(child.clone()) }) } else { resultGroup.add(convertedModel) } // 可选:添加坐标系参考轴 if (showAxes) { const axes = createCoordinateAxes(axesSize) resultGroup.add(axes) } return resultGroup }