* Parse FBX content * 解析 FBX 内容
(content: IAssetContent, context: IAssetParseContext)
| 158 | * 解析 FBX 内容 |
| 159 | */ |
| 160 | async parse(content: IAssetContent, context: IAssetParseContext): Promise<IGLTFAsset> { |
| 161 | const buffer = content.binary; |
| 162 | if (!buffer) { |
| 163 | throw new Error('FBX loader requires binary content'); |
| 164 | } |
| 165 | |
| 166 | // Detect format (binary or ASCII) |
| 167 | // 检测格式(二进制或 ASCII) |
| 168 | // FBX binary header is "Kaydara FBX Binary \0" (21 bytes + null) |
| 169 | // FBX 二进制头是 "Kaydara FBX Binary \0"(21字节 + 空字节) |
| 170 | const headerBytes = new Uint8Array(buffer, 0, Math.min(21, buffer.byteLength)); |
| 171 | const headerString = String.fromCharCode(...headerBytes); |
| 172 | const isBinary = headerString.startsWith('Kaydara FBX Binary'); |
| 173 | |
| 174 | let geometries: FBXGeometry[]; |
| 175 | let models: FBXModel[]; |
| 176 | let materials: FBXMaterial[]; |
| 177 | let animStacks: FBXAnimationStack[] = []; |
| 178 | let deformers: FBXDeformer[] = []; |
| 179 | let connections: FBXConnection[] = []; |
| 180 | |
| 181 | if (isBinary) { |
| 182 | const result = this.parseBinary(buffer); |
| 183 | geometries = result.geometries; |
| 184 | models = result.models; |
| 185 | materials = result.materials; |
| 186 | animStacks = result.animStacks; |
| 187 | deformers = result.deformers; |
| 188 | connections = result.connections; |
| 189 | } else { |
| 190 | // Try ASCII parsing (no animation support for ASCII yet) |
| 191 | // 尝试 ASCII 解析(ASCII 格式暂不支持动画) |
| 192 | const text = new TextDecoder().decode(buffer); |
| 193 | const result = this.parseASCII(text); |
| 194 | geometries = result.geometries; |
| 195 | models = result.models; |
| 196 | materials = result.materials; |
| 197 | } |
| 198 | |
| 199 | // Build skeleton data FIRST to get cluster -> joint index mapping |
| 200 | // 先构建骨骼数据以获取簇->关节索引映射 |
| 201 | const clusterToJointIndex = new Map<bigint, number>(); |
| 202 | const skeleton = this.buildSkeletonData(deformers, models, connections, clusterToJointIndex) ?? undefined; |
| 203 | |
| 204 | // Convert to mesh data with skinning (using the cluster mapping) |
| 205 | // 转换为带蒙皮的网格数据(使用簇映射) |
| 206 | const meshes = this.buildMeshes(geometries, deformers, connections, clusterToJointIndex); |
| 207 | |
| 208 | // Build material list |
| 209 | // 构建材质列表 |
| 210 | const gltfMaterials = this.buildMaterials(materials); |
| 211 | |
| 212 | // Build geometry ID to mesh index map | 构建几何体ID到网格索引的映射 |
| 213 | const geometryToMeshIndex = new Map<bigint, number>(); |
| 214 | geometries.forEach((geom, index) => { |
| 215 | if (index < meshes.length) { |
| 216 | geometryToMeshIndex.set(geom.id, index); |
| 217 | } |
no test coverage detected