threenative-asset-mcp 0.9.4 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -351,6 +351,7 @@ require manually installed tools instead.
351
351
  | Cooked loose UE4 static meshes and textures | GLB geometry, LOD sections, embedded textures, and common PBR reconstruction |
352
352
  | Cooked loose UE4 skeletal meshes and standalone animation packages | Standard skinned GLB; joints, weights, inverse-bind matrices, and existing clips are preserved; ActorX PSA clips are attached by case-insensitive bone name when at least 80% of tracks match, without duplicating an existing clip name |
353
353
  | Uncooked UE4 `FMeshDescription` static meshes (object versions 517–522) | GLB geometry with centimetres converted to metres; verified on Fab Office Pack Vol.1 (47/47 meshes) |
354
+ | Uncooked UE5 editor static meshes (source models only, no render data) | LOD0 of the `FMeshDescription` source model is decoded from the package trailer's Oodle payloads, with the cooked glTF writer's axes, scale, and material naming. Verified on Fab's Megascans Common Hazel and European Hornbeam UE5.1 artifacts: triangle counts match the UE4.27 decoder of the same scans exactly |
354
355
  | Uncooked UE4.0–4.20 static and skeletal meshes (object versions below 517) | Exported by UE Viewer from the editor source data, with materials, skins, and ActorX clips like cooked packages. Verified on Fab packs from UE4.10 to UE4.20: Modular Building Set (129 meshes), Animal Variety Pack (6 rigged animals, 146 clips), STF Landscape Pro 2.0 (61), Power Plant (65), and a UE4.19 fern pack (38) |
355
356
  | Standalone `Texture2D` packages | Collision-free source-relative PNGs under `textures/`; duplicate Unreal basenames are exported independently instead of overwriting one another |
356
357
  | `TextureCube` environment maps | Collision-free 2:1 equirectangular PNG or lossless Radiance HDR files under `cubemaps/`; CUE4Parse decodes cooked faces, while editor `TSF_BGRE8` source art retains its lighting range without Unreal Engine |
@@ -363,7 +364,7 @@ require manually installed tools instead.
363
364
  | Paper2D `PaperTileSet` and `PaperTileMap` packages | Populated layers become indexed, unlit GLB quads with atlas UVs, empty cells omitted, layer order retained, and packed horizontal/vertical/diagonal tile flips decoded. Placed tile maps and inherited Blueprint flipbook components are reconstructed in modern UE5 levels |
364
365
  | Paper2D grouped sprites and `TextRenderActor` | Grouped sprite instances become `EXT_mesh_gpu_instancing` batches over shared sprite GLBs; text becomes unlit glyph geometry from offline `UFont` atlas pages, preserving alignment, colour, world size, and placement |
365
366
  | `DataTable`, `StringTable`, `CurveTable`, and float/vector/colour curves | Collision-free, directly fetchable JSON under `data/`; row names, typed fields, localized strings, asset references, and rich-curve keys/tangents are retained |
366
- | Modern UE5 loose static meshes, skeletal meshes, `Texture2D`, Material, and `.umap` packages rejected by UE Viewer | CUE4Parse converts directly to the same output/report pipeline; verified with UE5.5 cooked static geometry, UE5 editor skeletal geometry, Oodle-compressed UE5.3 editor texture source art, and a UE5.2 Paper2D map. Cooked packages using unversioned properties require one matching `.usmap` file beside the imported tree |
367
+ | Modern UE5 loose static meshes, skeletal meshes, `Texture2D`, Material, and `.umap` packages rejected by UE Viewer | CUE4Parse converts directly to the same output/report pipeline; verified with UE5.5 cooked static geometry, UE5 editor skeletal geometry, Oodle-compressed UE5.3 editor texture source art, and a UE5.2 Paper2D map. Editor source art is restored to display colour: `TSF_BGRA8` sources are stored with red and blue swapped, and sRGB-sampled `TSF_RGBA16` sources are linear, so both are corrected (checked against UE Viewer's export of the same textures from the UE4.27 artifact). Cooked packages using unversioned properties require one matching `.usmap` file beside the imported tree |
367
368
  | Separate roughness/metalness, solid palette maps, glass, and mirrors | Packed glTF PBR maps or explicit material fallbacks; extra graph inputs remain named sidecars |
368
369
  | UE4 `.umap` placement, direct lights, and serialized Blueprint component templates (object versions 517–522) | Directly loadable scene GLB plus a transform/source manifest; repeated actors instance shared meshes; inherited mesh/light defaults are merged into placed instances; directional, point, and spot lights use `KHR_lights_punctual` |
369
370
  | Standalone modern UE5 Blueprint prefabs | Serialized static/skeletal mesh and light component defaults become directly loadable scene GLBs; referenced meshes, transforms, skins, and bone hierarchies are retained, while bytecode is explicitly reported and never executed |
@@ -35,6 +35,8 @@ export interface PackageCooking {
35
35
  readonly levelHint: boolean;
36
36
  /** Serialized BlueprintGeneratedClass/SCS prefab hint for newer headers UE Viewer rejects. */
37
37
  readonly blueprintPrefabHint: boolean;
38
+ /** An editor-only class with nothing to import, named when UE Viewer cannot list the package. */
39
+ readonly nonImportableClassHint: string | undefined;
38
40
  }
39
41
  /** Object names a package names in the name and import tables at its head: the packages it
40
42
  * references, such as a static mesh's materials and textures. Those are separate files on disk. */
@@ -1 +1 @@
1
- {"version":3,"file":"cooking.d.ts","sourceRoot":"","sources":["../../src/unreal/cooking.ts"],"names":[],"mappings":"AAuBA;;;oFAGoF;AACpF,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,4FAA4F;IAC5F,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,mGAAmG;IACnG,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wFAAwF;IACxF,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,mGAAmG;IACnG,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,YAAY,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IACzD,2FAA2F;IAC3F,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,6FAA6F;IAC7F,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,oGAAoG;IACpG,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,yFAAyF;IACzF,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;IACnD,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,iGAAiG;IACjG,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8FAA8F;IAC9F,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;CACvC;AAyCD;mGACmG;AACnG,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAU/E;AAED;;;;;gGAKgG;AAChG,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAwD9E"}
1
+ {"version":3,"file":"cooking.d.ts","sourceRoot":"","sources":["../../src/unreal/cooking.ts"],"names":[],"mappings":"AAuBA;;;oFAGoF;AACpF,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,4FAA4F;IAC5F,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,mGAAmG;IACnG,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wFAAwF;IACxF,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,mGAAmG;IACnG,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,YAAY,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IACzD,2FAA2F;IAC3F,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,6FAA6F;IAC7F,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,oGAAoG;IACpG,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,yFAAyF;IACzF,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;IACnD,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,iGAAiG;IACjG,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,8FAA8F;IAC9F,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,iGAAiG;IACjG,QAAQ,CAAC,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;CACrD;AA8CD;mGACmG;AACnG,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAU/E;AAED;;;;;gGAKgG;AAChG,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA4D9E"}
@@ -17,6 +17,9 @@ const EDITOR_ONLY_MARKERS = Object.freeze([
17
17
  "RawMeshBulkData",
18
18
  "MeshDescriptionBulkData",
19
19
  ]);
20
+ /** Editor-only asset classes a UE5 pack ships beside its meshes. UE Viewer cannot list them, so
21
+ * without this they read as failed packages rather than as content with nothing to import. */
22
+ const NON_IMPORTABLE_CLASSES = ["FoliageType_InstancedStaticMesh", "MaterialFunction", "MaterialParameterCollection"];
20
23
  /** What a file that is not a readable package reports: no signal, never a negative one. */
21
24
  const UNKNOWN = Object.freeze({
22
25
  state: "unknown",
@@ -35,6 +38,7 @@ const UNKNOWN = Object.freeze({
35
38
  materialHint: false,
36
39
  levelHint: false,
37
40
  blueprintPrefabHint: false,
41
+ nonImportableClassHint: undefined,
38
42
  });
39
43
  /** Reads the bounded prefix of one package, or undefined when it cannot be read. */
40
44
  async function readHead(file) {
@@ -105,6 +109,9 @@ export async function readPackageCooking(file) {
105
109
  (head.includes("MaterialInstanceBasePropertyOverrides", 0, "latin1") && head.includes("MaterialInstanceConstant", 0, "latin1"));
106
110
  const levelHint = file.toLowerCase().endsWith(".umap") &&
107
111
  head.includes("PersistentLevel", 0, "latin1") && head.includes("WorldSettings", 0, "latin1");
112
+ // A whole name-table entry: the length prefix's high byte and the NUL terminator bracket it,
113
+ // so "MaterialFunction" does not match inside "MaterialFunctionInstance".
114
+ const nonImportableClassHint = NON_IMPORTABLE_CLASSES.find((className) => head.includes(`\0${className}\0`, 0, "latin1"));
108
115
  const blueprintPrefabHint = file.toLowerCase().endsWith(".uasset") &&
109
116
  head.includes("BlueprintGeneratedClass", 0, "latin1") && head.includes("SimpleConstructionScript", 0, "latin1");
110
117
  return {
@@ -132,6 +139,7 @@ export async function readPackageCooking(file) {
132
139
  materialHint,
133
140
  levelHint,
134
141
  blueprintPrefabHint,
142
+ nonImportableClassHint,
135
143
  };
136
144
  }
137
145
  //# sourceMappingURL=cooking.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cooking.js","sourceRoot":"","sources":["../../src/unreal/cooking.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cooking.js","sourceRoot":"","sources":["../../src/unreal/cooking.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  export declare const CUE4PARSE_SOURCE: Readonly<{
3
3
  repository: "https://github.com/FabianFG/CUE4Parse.git";
4
4
  commit: "b4e95441bcf0c975eb3adb68c0fb44c740c2cf62";
5
- version: "b4e95441+threenative.48";
5
+ version: "b4e95441+threenative.49";
6
6
  }>;
7
7
  /** Applied to the pinned checkout, which remains an out-of-process Apache-2.0 tool. */
8
8
  export declare const CUE4PARSE_PATCH: string;
@@ -1 +1 @@
1
- {"version":3,"file":"cue4parse-adapter.d.ts","sourceRoot":"","sources":["../../src/unreal/cue4parse-adapter.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,eAAO,MAAM,gBAAgB;;;;EAI3B,CAAC;AAEH,uFAAuF;AACvF,eAAO,MAAM,eAAe,QAsM3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAc7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAqvC7B,CAAC"}
1
+ {"version":3,"file":"cue4parse-adapter.d.ts","sourceRoot":"","sources":["../../src/unreal/cue4parse-adapter.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,eAAO,MAAM,gBAAgB;;;;EAI3B,CAAC;AAEH,uFAAuF;AACvF,eAAO,MAAM,eAAe,QAsM3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAc7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAskD7B,CAAC"}
@@ -2,7 +2,7 @@
2
2
  export const CUE4PARSE_SOURCE = Object.freeze({
3
3
  repository: "https://github.com/FabianFG/CUE4Parse.git",
4
4
  commit: "b4e95441bcf0c975eb3adb68c0fb44c740c2cf62",
5
- version: "b4e95441+threenative.48",
5
+ version: "b4e95441+threenative.49",
6
6
  });
7
7
  /** Applied to the pinned checkout, which remains an out-of-process Apache-2.0 tool. */
8
8
  export const CUE4PARSE_PATCH = String.raw `diff --git a/Directory.Packages.props b/Directory.Packages.props
@@ -220,6 +220,7 @@ export const CUE4PARSE_PROJECT = String.raw `<Project Sdk="Microsoft.NET.Sdk">
220
220
  </Project>
221
221
  `;
222
222
  export const CUE4PARSE_PROGRAM = String.raw `using System.Buffers.Binary;
223
+ using System.Runtime.InteropServices;
223
224
  using System.Text;
224
225
  using CUE4Parse.FileProvider;
225
226
  using CUE4Parse.MappingsProvider.Usmap;
@@ -330,7 +331,7 @@ async Task<bool> ExportPaperSpriteAsync(UPaperSprite sprite)
330
331
  var packageBytes = await File.ReadAllBytesAsync(textureFile);
331
332
  var sourcePng = ExtractLargestPng(packageBytes) ?? ExtractCompressedPayloadPng(packageBytes);
332
333
  if (sourcePng is null) return false;
333
- await File.WriteAllBytesAsync(textureTarget, sourcePng);
334
+ await File.WriteAllBytesAsync(textureTarget, NormalizeSourcePng(sourcePng, LoadAssetByName<UTexture>(textureName)));
334
335
  }
335
336
  var descriptor = new {
336
337
  Name = sprite.Name,
@@ -461,10 +462,16 @@ async Task ExportMaterialAsync(string initialName)
461
462
 
462
463
  foreach (var textureName in references.Select(entry => entry.Texture).Distinct(StringComparer.OrdinalIgnoreCase))
463
464
  {
465
+ var target = Path.Combine(materialDirectory, textureName + ".png");
466
+ // Materials share the same 8K textures, so skip decoding/encoding one another material already wrote.
467
+ if (File.Exists(target)) continue;
464
468
  var textureFile = Directory.EnumerateFiles(root, textureName + ".uasset", SearchOption.AllDirectories).FirstOrDefault();
465
469
  if (textureFile is null || new FileInfo(textureFile).Length > 1_073_741_824) continue;
466
- var sourcePng = ExtractLargestPng(await File.ReadAllBytesAsync(textureFile));
467
- if (sourcePng is not null) await File.WriteAllBytesAsync(Path.Combine(materialDirectory, textureName + ".png"), sourcePng);
470
+ // UE5.1+ keeps large source art inside an FCompressedBuffer payload, like every other
471
+ // editor texture site; without this fallback a level's materials lost their textures.
472
+ var textureBytes = await File.ReadAllBytesAsync(textureFile);
473
+ var sourcePng = ExtractLargestPng(textureBytes) ?? ExtractCompressedPayloadPng(textureBytes);
474
+ if (sourcePng is not null) await File.WriteAllBytesAsync(target, NormalizeSourcePng(sourcePng, LoadAssetByName<UTexture>(textureName)));
468
475
  }
469
476
  }
470
477
  }
@@ -474,22 +481,76 @@ async Task<bool> ExportStaticMeshAsync(UStaticMesh mesh)
474
481
  var identity = mesh.GetPathName();
475
482
  var target = Path.Combine(output, "Meshes", mesh.Name + ".glb");
476
483
  if (!exportedMeshes.Add(identity)) return File.Exists(target);
477
- var session = new ExportSession { MaxDegreeOfParallelism = 1 };
478
- session.Add(mesh);
479
- var results = await session.RunAsync(output, new ExportOptions(meshFormat: EMeshFormat.Gltf2, exportMaterials: false));
480
- var emitted = results.SelectMany(result => result.DiskFilePaths ?? [])
481
- .FirstOrDefault(path => path.EndsWith(".glb", StringComparison.OrdinalIgnoreCase));
482
- if (emitted is null || !File.Exists(emitted))
484
+ // An uncooked UE5 package deserializes StaticMaterials as a tagged property, after the point
485
+ // UStaticMesh stops reading editor packages.
486
+ var staticMaterials = mesh.StaticMaterials.Length > 0
487
+ ? mesh.StaticMaterials
488
+ : mesh.GetOrDefault("StaticMaterials", Array.Empty<FStaticMaterial>());
489
+ if (mesh.RenderData?.LODs is not { Length: > 0 })
483
490
  {
484
- exportedMeshes.Remove(identity);
485
- return false;
491
+ // Uncooked editor mesh: no render data, only the source model. Decode LOD0's
492
+ // FMeshDescription from the package trailer instead.
493
+ if (!ExportEditorStaticMesh(mesh, staticMaterials, target))
494
+ {
495
+ exportedMeshes.Remove(identity);
496
+ return false;
497
+ }
486
498
  }
487
- if (!Path.GetFullPath(emitted).Equals(Path.GetFullPath(target), StringComparison.OrdinalIgnoreCase)) File.Move(emitted, target, true);
488
- foreach (var materialName in mesh.StaticMaterials.Select(slot => slot.MaterialInterface?.Name).Where(name => !string.IsNullOrWhiteSpace(name)).Distinct(StringComparer.OrdinalIgnoreCase))
499
+ else
500
+ {
501
+ var session = new ExportSession { MaxDegreeOfParallelism = 1 };
502
+ session.Add(mesh);
503
+ var results = await session.RunAsync(output, new ExportOptions(meshFormat: EMeshFormat.Gltf2, exportMaterials: false));
504
+ var emitted = results.SelectMany(result => result.DiskFilePaths ?? [])
505
+ .FirstOrDefault(path => path.EndsWith(".glb", StringComparison.OrdinalIgnoreCase));
506
+ if (emitted is null || !File.Exists(emitted))
507
+ {
508
+ exportedMeshes.Remove(identity);
509
+ return false;
510
+ }
511
+ if (!Path.GetFullPath(emitted).Equals(Path.GetFullPath(target), StringComparison.OrdinalIgnoreCase)) File.Move(emitted, target, true);
512
+ }
513
+ foreach (var materialName in staticMaterials.Select(slot => slot.MaterialInterface?.Name).Where(name => !string.IsNullOrWhiteSpace(name)).Distinct(StringComparer.OrdinalIgnoreCase))
489
514
  await ExportMaterialAsync(materialName!);
490
515
  return true;
491
516
  }
492
517
 
518
+ bool ExportEditorStaticMesh(UStaticMesh mesh, FStaticMaterial[] staticMaterials, string target)
519
+ {
520
+ // Same-named meshes in different folders: prefer the key whose last two path segments match
521
+ // the owning package's.
522
+ var packageTail = string.Join('/', (mesh.Owner?.Name ?? mesh.Name).Split('/').TakeLast(2));
523
+ var packageKey = provider.Files.Keys
524
+ .Where(candidate => candidate.EndsWith(".uasset", StringComparison.OrdinalIgnoreCase) &&
525
+ Path.GetFileNameWithoutExtension(candidate).Equals(mesh.Name, StringComparison.OrdinalIgnoreCase))
526
+ .OrderByDescending(candidate => Path.ChangeExtension(candidate.Replace('\\', '/'), null)
527
+ .EndsWith(packageTail, StringComparison.OrdinalIgnoreCase))
528
+ .FirstOrDefault();
529
+ if (packageKey is null || !provider.Files.TryGetValue(packageKey, out var file) || file.Size > 2_000_000_000L)
530
+ {
531
+ assetLookupDiagnostics[mesh.Name] = "uncooked mesh package was not mounted";
532
+ return false;
533
+ }
534
+ var editorMesh = ReadLargestMeshDescription(file.Read());
535
+ if (editorMesh is null)
536
+ {
537
+ assetLookupDiagnostics[mesh.Name] = "uncooked mesh has no readable FMeshDescription source model";
538
+ return false;
539
+ }
540
+ // Same material naming as the cooked glTF writer (MeshMaterialDto.SlotName): the material
541
+ // interface's name, else the imported slot name.
542
+ var materialNames = editorMesh.GroupSlots.Select((slot, index) =>
543
+ {
544
+ var match = staticMaterials.FirstOrDefault(material =>
545
+ string.Equals(material.ImportedMaterialSlotName?.Text, slot, StringComparison.OrdinalIgnoreCase) ||
546
+ string.Equals(material.MaterialSlotName.Text, slot, StringComparison.OrdinalIgnoreCase))
547
+ ?? (index < staticMaterials.Length ? staticMaterials[index] : null);
548
+ return match?.MaterialInterface?.Name ?? match?.ImportedMaterialSlotName?.Text ?? slot;
549
+ }).ToArray();
550
+ WriteEditorMeshGlb(editorMesh, materialNames, mesh.Name, target);
551
+ return File.Exists(target);
552
+ }
553
+
493
554
  T? LoadAssetByName<T>(string name) where T : UObject
494
555
  {
495
556
  var assetKeys = provider.Files.Keys.Where(candidate =>
@@ -999,7 +1060,7 @@ foreach (var key in provider.Files.Keys.Where(key =>
999
1060
  packageBytes = await File.ReadAllBytesAsync(packageFile);
1000
1061
  }
1001
1062
  var sourcePng = ExtractLargestPng(packageBytes) ?? ExtractCompressedPayloadPng(packageBytes);
1002
- if (sourcePng is not null) await File.WriteAllBytesAsync(target, sourcePng);
1063
+ if (sourcePng is not null) await File.WriteAllBytesAsync(target, NormalizeSourcePng(sourcePng, texture));
1003
1064
  }
1004
1065
  if (File.Exists(target)) pageFiles.Add(targetName);
1005
1066
  }
@@ -1061,7 +1122,7 @@ foreach (var key in provider.Files.Keys.Where(key =>
1061
1122
  var packageBytes = await File.ReadAllBytesAsync(textureFile);
1062
1123
  var sourcePng = ExtractLargestPng(packageBytes) ?? ExtractCompressedPayloadPng(packageBytes);
1063
1124
  if (sourcePng is null) continue;
1064
- await File.WriteAllBytesAsync(target, sourcePng);
1125
+ await File.WriteAllBytesAsync(target, NormalizeSourcePng(sourcePng, texture));
1065
1126
  }
1066
1127
  exported++;
1067
1128
  }
@@ -1166,7 +1227,7 @@ foreach (var key in provider.Files.Keys.Where(key =>
1166
1227
  if (sourceFile is not null && new FileInfo(sourceFile).Length <= 1_073_741_824)
1167
1228
  {
1168
1229
  var sourcePng = ExtractLargestPng(await File.ReadAllBytesAsync(sourceFile)) ?? ExtractCompressedPayloadPng(await File.ReadAllBytesAsync(sourceFile));
1169
- if (sourcePng is not null) await File.WriteAllBytesAsync(textureTarget, sourcePng);
1230
+ if (sourcePng is not null) await File.WriteAllBytesAsync(textureTarget, NormalizeSourcePng(sourcePng, sheet));
1170
1231
  }
1171
1232
  }
1172
1233
  if (!File.Exists(textureTarget)) textureFileName = null;
@@ -1356,6 +1417,272 @@ static byte[]? ExtractCompressedPayloadPng(byte[] bytes)
1356
1417
  return null;
1357
1418
  }
1358
1419
 
1420
+ // The LOD0 source model of an uncooked UE5 StaticMesh. Its package trailer holds one
1421
+ // FMeshDescription payload per source-model LOD, so the largest raw payload that parses is LOD0.
1422
+ // Every other payload in a StaticMesh package is also a mesh description, so ranking by the
1423
+ // header's raw size decompresses only what is kept.
1424
+ static EditorMesh? ReadLargestMeshDescription(byte[] bytes)
1425
+ {
1426
+ ReadOnlySpan<byte> magic = [0xb7, 0x75, 0x63, 0x62];
1427
+ var candidates = new List<(int At, ulong RawSize)>();
1428
+ for (var searchAt = 0; searchAt <= bytes.Length - magic.Length;)
1429
+ {
1430
+ var relativeAt = bytes.AsSpan(searchAt).IndexOf(magic);
1431
+ if (relativeAt < 0) break;
1432
+ var payloadAt = searchAt + relativeAt;
1433
+ searchAt = payloadAt + magic.Length;
1434
+ try
1435
+ {
1436
+ using var archive = new FByteArchive("editor-payload", bytes);
1437
+ archive.Position = payloadAt;
1438
+ var header = new FCompressedBufferHeader(archive);
1439
+ if (header.Magic == FCompressedBufferHeader.ExpectedMagic && header.TotalRawSize is > 0 and <= 1_073_741_824)
1440
+ candidates.Add((payloadAt, header.TotalRawSize));
1441
+ }
1442
+ catch
1443
+ {
1444
+ // The magic may occur inside unrelated compressed bytes.
1445
+ }
1446
+ }
1447
+ foreach (var (at, _) in candidates.OrderByDescending(candidate => candidate.RawSize))
1448
+ {
1449
+ try
1450
+ {
1451
+ using var archive = new FByteArchive("editor-payload", bytes);
1452
+ archive.Position = at;
1453
+ return ReadMeshDescription(DecompressEditorPayload(new FCompressedBuffer(archive)));
1454
+ }
1455
+ catch
1456
+ {
1457
+ // Not a mesh description; try the next largest payload.
1458
+ }
1459
+ }
1460
+ return null;
1461
+ }
1462
+
1463
+ // UE5's FMeshDescription serialization, as verified byte-exact on the Common Hazel packs (see
1464
+ // docs/PRDs/done/ue5-mesh-description-reference.py for the annotated layout). Throws unless the
1465
+ // whole payload is consumed, so a layout drift is a refusal rather than garbage geometry.
1466
+ static EditorMesh ReadMeshDescription(byte[] raw)
1467
+ {
1468
+ using var stream = new MemoryStream(raw, false);
1469
+ using var reader = new BinaryReader(stream);
1470
+ string ReadName()
1471
+ {
1472
+ var length = reader.ReadInt32();
1473
+ if (length is <= 0 or > 4096) throw new InvalidDataException("Implausible FString length.");
1474
+ var text = Encoding.ASCII.GetString(reader.ReadBytes(length - 1));
1475
+ reader.ReadByte();
1476
+ return text;
1477
+ }
1478
+ var arrays = new Dictionary<string, byte[]>(StringComparer.Ordinal);
1479
+ var names = new Dictionary<string, string[]>(StringComparer.Ordinal);
1480
+ var live = new Dictionary<string, bool[]>(StringComparer.Ordinal);
1481
+ var elementTypes = reader.ReadInt32();
1482
+ if (elementTypes is < 1 or > 64) throw new InvalidDataException("Not a mesh description.");
1483
+ for (var elementIndex = 0; elementIndex < elementTypes; elementIndex++)
1484
+ {
1485
+ var element = ReadName();
1486
+ var channels = reader.ReadInt32();
1487
+ if (channels is < 0 or > 64) throw new InvalidDataException("Implausible element channel count.");
1488
+ for (var channel = 0; channel < channels; channel++)
1489
+ {
1490
+ var bits = reader.ReadInt32();
1491
+ if (bits < 0) throw new InvalidDataException("Negative allocation bit count.");
1492
+ var words = new uint[(bits + 31) / 32];
1493
+ for (var word = 0; word < words.Length; word++) words[word] = reader.ReadUInt32();
1494
+ if (channel == 0) live[element] = Enumerable.Range(0, bits).Select(bit => ((words[bit >> 5] >> (bit & 31)) & 1) != 0).ToArray();
1495
+ reader.ReadInt32(); // free-list head
1496
+ reader.ReadInt32(); // element count
1497
+ var attributes = reader.ReadInt32();
1498
+ for (var attributeIndex = 0; attributeIndex < attributes; attributeIndex++)
1499
+ {
1500
+ var attribute = ReadName().Trim(); // Vertex position is "Position " with a space
1501
+ var kind = reader.ReadInt32();
1502
+ reader.ReadInt32();
1503
+ reader.ReadInt32();
1504
+ var indices = reader.ReadInt32();
1505
+ for (var index = 0; index < indices; index++)
1506
+ {
1507
+ reader.ReadInt32(); // extent
1508
+ var key = element + "[" + channel + "]." + attribute + "[" + index + "]";
1509
+ if (kind == 6)
1510
+ {
1511
+ var count = reader.ReadInt32();
1512
+ names[key] = Enumerable.Range(0, count).Select(_ => ReadName()).ToArray();
1513
+ continue;
1514
+ }
1515
+ var elementSize = reader.ReadInt32();
1516
+ var elements = reader.ReadInt32();
1517
+ arrays[key] = reader.ReadBytes(checked(elementSize * elements));
1518
+ }
1519
+ // The default value is written even for an attribute with no channels, so its size
1520
+ // comes from the attribute type: FVector4f, FVector3f, FVector2f, float, int32, and
1521
+ // bool (a 4-byte UE bool, although bulk arrays store 1 byte per element).
1522
+ if (kind == 6) ReadName();
1523
+ else reader.ReadBytes(kind switch
1524
+ {
1525
+ 0 => 16, 1 => 12, 2 => 8, 3 or 4 or 5 => 4,
1526
+ _ => throw new InvalidDataException("Unknown mesh attribute type " + kind + "."),
1527
+ });
1528
+ reader.ReadInt32(); // EMeshAttributeFlags
1529
+ }
1530
+ }
1531
+ }
1532
+ if (stream.Position != raw.Length) throw new InvalidDataException("Mesh description was not fully consumed.");
1533
+ float[] Floats(string key) => arrays.TryGetValue(key, out var bytes) ? MemoryMarshal.Cast<byte, float>(bytes).ToArray() : throw new InvalidDataException("Missing " + key);
1534
+ int[] Ints(string key) => arrays.TryGetValue(key, out var bytes) ? MemoryMarshal.Cast<byte, int>(bytes).ToArray() : throw new InvalidDataException("Missing " + key);
1535
+ return new EditorMesh(
1536
+ Floats("Vertices[0].Position[0]"),
1537
+ Ints("VertexInstances[0].VertexIndex[0]"),
1538
+ Floats("VertexInstances[0].Normal[0]"),
1539
+ Floats("VertexInstances[0].TextureCoordinate[0]"),
1540
+ Ints("Triangles[0].VertexInstanceIndex[0]"),
1541
+ Ints("Triangles[0].PolygonGroupIndex[0]"),
1542
+ live.TryGetValue("Triangles", out var triangles) ? triangles : [],
1543
+ names.TryGetValue("PolygonGroups[0].ImportedMaterialSlotName[0]", out var slots) ? slots : []);
1544
+ }
1545
+
1546
+ // Writes the decoded source model with the cooked glTF writer's conventions: centimetres to metres,
1547
+ // Unreal's Z-up swapped to glTF's Y-up, and Unreal's triangle order kept (the axis swap and
1548
+ // Unreal's left-handedness cancel). Vertex colours and tangents are left out, as the UE Viewer
1549
+ // route leaves them out: Megascans vertex colours are wind masks, not tint, and glTF clients
1550
+ // generate MikkTSpace tangents when none are given.
1551
+ static void WriteEditorMeshGlb(EditorMesh mesh, string[] materialNames, string name, string target)
1552
+ {
1553
+ var builder = new SharpGLTF.Geometry.MeshBuilder<
1554
+ SharpGLTF.Geometry.VertexTypes.VertexPositionNormal,
1555
+ SharpGLTF.Geometry.VertexTypes.VertexTexture1,
1556
+ SharpGLTF.Geometry.VertexTypes.VertexEmpty>(name);
1557
+ var materials = new Dictionary<int, SharpGLTF.Materials.MaterialBuilder>();
1558
+ SharpGLTF.Materials.MaterialBuilder Material(int group)
1559
+ {
1560
+ if (materials.TryGetValue(group, out var existing)) return existing;
1561
+ var material = new SharpGLTF.Materials.MaterialBuilder(group >= 0 && group < materialNames.Length ? materialNames[group] : "MaterialSlot_" + group)
1562
+ .WithBaseColor(System.Numerics.Vector4.One);
1563
+ materials[group] = material;
1564
+ return material;
1565
+ }
1566
+ SharpGLTF.Geometry.VertexBuilder<
1567
+ SharpGLTF.Geometry.VertexTypes.VertexPositionNormal,
1568
+ SharpGLTF.Geometry.VertexTypes.VertexTexture1,
1569
+ SharpGLTF.Geometry.VertexTypes.VertexEmpty> Vertex(int instance)
1570
+ {
1571
+ var vertex = mesh.InstanceVertices[instance];
1572
+ var position = new System.Numerics.Vector3(mesh.Positions[vertex * 3], mesh.Positions[vertex * 3 + 2], mesh.Positions[vertex * 3 + 1]) * 0.01f;
1573
+ var normal = new System.Numerics.Vector3(mesh.Normals[instance * 3], mesh.Normals[instance * 3 + 2], mesh.Normals[instance * 3 + 1]);
1574
+ normal = normal.LengthSquared() > 1e-12f && float.IsFinite(normal.LengthSquared()) ? System.Numerics.Vector3.Normalize(normal) : System.Numerics.Vector3.UnitY;
1575
+ var uv = new System.Numerics.Vector2(mesh.Uv0[instance * 2], mesh.Uv0[instance * 2 + 1]);
1576
+ return new(new SharpGLTF.Geometry.VertexTypes.VertexPositionNormal(position, normal), new SharpGLTF.Geometry.VertexTypes.VertexTexture1(uv));
1577
+ }
1578
+ var triangleCount = mesh.TriangleInstances.Length / 3;
1579
+ for (var triangle = 0; triangle < triangleCount; triangle++)
1580
+ {
1581
+ if (triangle < mesh.LiveTriangles.Length && !mesh.LiveTriangles[triangle]) continue;
1582
+ var group = triangle < mesh.TriangleGroups.Length ? mesh.TriangleGroups[triangle] : 0;
1583
+ builder.UsePrimitive(Material(group)).AddTriangle(
1584
+ Vertex(mesh.TriangleInstances[triangle * 3]),
1585
+ Vertex(mesh.TriangleInstances[triangle * 3 + 1]),
1586
+ Vertex(mesh.TriangleInstances[triangle * 3 + 2]));
1587
+ }
1588
+ var scene = new SharpGLTF.Scenes.SceneBuilder();
1589
+ scene.AddRigidMesh(builder, System.Numerics.Matrix4x4.Identity);
1590
+ Directory.CreateDirectory(Path.GetDirectoryName(target)!);
1591
+ scene.ToGltf2().SaveGLB(target);
1592
+ }
1593
+
1594
+ // Editor source art is stored the way FTextureSource keeps it, not as a display image. A
1595
+ // TSF_BGRA8 source PNG carries blue in its red channel: against UE Viewer's export of the same
1596
+ // Megascans texture, the extracted PNG measured RMSE 0.066 as-is and 0.016 with red and blue
1597
+ // swapped. TSF_RGBA16 source is linear light, so an sRGB-sampled one reads about four times too
1598
+ // dark until it is encoded to sRGB. Anything else passes through unchanged.
1599
+ static byte[] NormalizeSourcePng(byte[] png, UTexture? texture)
1600
+ {
1601
+ if (texture is null) return png;
1602
+ var format = texture.GetOrDefault<FStructFallback?>("Source", null)?.GetOrDefault<FName>("Format").Text ?? "";
1603
+ try
1604
+ {
1605
+ if (format.EndsWith("TSF_BGRA8", StringComparison.Ordinal)) return SwapRedBlue(png);
1606
+ if (format.EndsWith("TSF_RGBA16", StringComparison.Ordinal) && texture.SRGB) return LinearToSrgb8(png);
1607
+ }
1608
+ catch (Exception error) when (error is not OutOfMemoryException)
1609
+ {
1610
+ // An undecodable PNG is kept as extracted rather than dropped.
1611
+ }
1612
+ return png;
1613
+ }
1614
+
1615
+ static byte[] SwapRedBlue(byte[] png)
1616
+ {
1617
+ using var codec = SkiaSharp.SKCodec.Create(new MemoryStream(png)) ?? throw new InvalidDataException("Not a PNG.");
1618
+ var info = new SkiaSharp.SKImageInfo(codec.Info.Width, codec.Info.Height, SkiaSharp.SKColorType.Rgba8888, SkiaSharp.SKAlphaType.Unpremul);
1619
+ var pixels = new byte[info.BytesSize];
1620
+ var handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
1621
+ try
1622
+ {
1623
+ if (codec.GetPixels(info, handle.AddrOfPinnedObject()) != SkiaSharp.SKCodecResult.Success) throw new InvalidDataException("PNG decode failed.");
1624
+ for (var index = 0; index < pixels.Length; index += 4) (pixels[index], pixels[index + 2]) = (pixels[index + 2], pixels[index]);
1625
+ using var pixmap = new SkiaSharp.SKPixmap(info, handle.AddrOfPinnedObject());
1626
+ using var encoded = pixmap.Encode(SkiaSharp.SKEncodedImageFormat.Png, 100) ?? throw new InvalidDataException("PNG encode failed.");
1627
+ return encoded.ToArray();
1628
+ }
1629
+ finally
1630
+ {
1631
+ handle.Free();
1632
+ }
1633
+ }
1634
+
1635
+ // ponytail: decodes the whole 16-bit image at once (an 8K source needs ~800 MB); decode by
1636
+ // scanline if that ever exhausts memory.
1637
+ static byte[] LinearToSrgb8(byte[] png)
1638
+ {
1639
+ using var codec = SkiaSharp.SKCodec.Create(new MemoryStream(png)) ?? throw new InvalidDataException("Not a PNG.");
1640
+ var width = codec.Info.Width;
1641
+ var height = codec.Info.Height;
1642
+ // Skia's PNG codec refuses a 16-bit unorm target (InvalidConversion) but decodes to half
1643
+ // floats, which hold every value an 8-bit result can distinguish. With no colour space on the
1644
+ // target it applies no conversion, so these are the stored linear values.
1645
+ var wide = new SkiaSharp.SKImageInfo(width, height, SkiaSharp.SKColorType.RgbaF16, SkiaSharp.SKAlphaType.Unpremul);
1646
+ var source = new Half[(long) width * height * 4];
1647
+ var lut = new byte[65536];
1648
+ for (var value = 0; value < lut.Length; value++)
1649
+ {
1650
+ var linear = value / 65535.0;
1651
+ var srgb = linear <= 0.0031308 ? linear * 12.92 : 1.055 * Math.Pow(linear, 1 / 2.4) - 0.055;
1652
+ lut[value] = (byte) Math.Clamp(Math.Round(srgb * 255), 0, 255);
1653
+ }
1654
+ var sourceHandle = GCHandle.Alloc(source, GCHandleType.Pinned);
1655
+ try
1656
+ {
1657
+ if (codec.GetPixels(wide, sourceHandle.AddrOfPinnedObject()) != SkiaSharp.SKCodecResult.Success) throw new InvalidDataException("PNG decode failed.");
1658
+ }
1659
+ finally
1660
+ {
1661
+ sourceHandle.Free();
1662
+ }
1663
+ var narrow = new SkiaSharp.SKImageInfo(width, height, SkiaSharp.SKColorType.Rgba8888, SkiaSharp.SKAlphaType.Unpremul);
1664
+ var output = new byte[narrow.BytesSize];
1665
+ static int Unorm16(Half value) => (int) Math.Clamp(Math.Round((float) value * 65535f), 0f, 65535f);
1666
+ for (long index = 0; index < source.LongLength; index += 4)
1667
+ {
1668
+ output[index] = lut[Unorm16(source[index])];
1669
+ output[index + 1] = lut[Unorm16(source[index + 1])];
1670
+ output[index + 2] = lut[Unorm16(source[index + 2])];
1671
+ output[index + 3] = (byte) Math.Clamp(Math.Round((float) source[index + 3] * 255f), 0f, 255f); // alpha is coverage, not light
1672
+ }
1673
+ var outputHandle = GCHandle.Alloc(output, GCHandleType.Pinned);
1674
+ try
1675
+ {
1676
+ using var pixmap = new SkiaSharp.SKPixmap(narrow, outputHandle.AddrOfPinnedObject());
1677
+ using var encoded = pixmap.Encode(SkiaSharp.SKEncodedImageFormat.Png, 100) ?? throw new InvalidDataException("PNG encode failed.");
1678
+ return encoded.ToArray();
1679
+ }
1680
+ finally
1681
+ {
1682
+ outputHandle.Free();
1683
+ }
1684
+ }
1685
+
1359
1686
  static byte[] DecompressEditorPayload(FCompressedBuffer payload)
1360
1687
  {
1361
1688
  var header = payload.Header;
@@ -1488,5 +1815,15 @@ public sealed class USkeletalMeshEditorData : UObject
1488
1815
  }
1489
1816
  }
1490
1817
  }
1818
+
1819
+ sealed record EditorMesh(
1820
+ float[] Positions,
1821
+ int[] InstanceVertices,
1822
+ float[] Normals,
1823
+ float[] Uv0,
1824
+ int[] TriangleInstances,
1825
+ int[] TriangleGroups,
1826
+ bool[] LiveTriangles,
1827
+ string[] GroupSlots);
1491
1828
  `;
1492
1829
  //# sourceMappingURL=cue4parse-adapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cue4parse-adapter.js","sourceRoot":"","sources":["../../src/unreal/cue4parse-adapter.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5C,UAAU,EAAE,2CAA2C;IACvD,MAAM,EAAE,0CAA0C;IAClD,OAAO,EAAE,yBAAyB;CACnC,CAAC,CAAC;AAEH,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsMxC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;CAc1C,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6EAmCkC,gBAAgB,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAktCpG,CAAC"}
1
+ {"version":3,"file":"cue4parse-adapter.js","sourceRoot":"","sources":["../../src/unreal/cue4parse-adapter.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5C,UAAU,EAAE,2CAA2C;IACvD,MAAM,EAAE,0CAA0C;IAClD,OAAO,EAAE,yBAAyB;CACnC,CAAC,CAAC;AAEH,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsMxC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;CAc1C,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6EAoCkC,gBAAgB,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkiDpG,CAAC"}
@@ -3,7 +3,7 @@ import { type TextureTransform } from "./materials.js";
3
3
  import { type ImportedScene } from "./scenes.js";
4
4
  import { type ExternalTool } from "./toolchain.js";
5
5
  /** Bumped whenever the conversion contract changes; it participates in the reuse cache key. */
6
- export declare const IMPORTER_VERSION = 46;
6
+ export declare const IMPORTER_VERSION = 47;
7
7
  /** Which tool decodes an uncooked mesh package, or undefined when it must be refused. */
8
8
  export declare function uncookedMeshRoute(meshKind: "static" | "skeletal", fileVersionUE4: number | undefined): "umodel" | "mesh-description" | "modern" | undefined;
9
9
  export type ImportErrorCode = "UNREAL_SOURCE_NOT_FOUND" | "UNREAL_SOURCE_EMPTY" | "UNREAL_OUTPUT_INVALID" | "UNREAL_OUTPUT_COLLISION" | "UNREAL_DISK_SPACE" | "UNREAL_EXPORT_EMPTY" | "UNREAL_SOURCE_UNCOOKED" | "UNREAL_SOURCE_UNSUPPORTED" | "UNREAL_GLB_INVALID";