feat(frostbite): add Star Wars Squadrons / Battlefront II support#23
Open
dhkatz wants to merge 1 commit into
Open
feat(frostbite): add Star Wars Squadrons / Battlefront II support#23dhkatz wants to merge 1 commit into
dhkatz wants to merge 1 commit into
Conversation
…ture extraction Adds a Frostbite asset path covering static and composite MeshAssets, TextureAssets, and the surrounding material binding for Squadrons (and SWBF II, sharing the same MeshSet RES layout). The Explorer can mount a game install and browse EBX entries; previewing a mesh produces a glTF with diffuse / normal / emissive textures resolved through the MeshVariationDatabase and SurfaceShaderPreset EBX entries. Pipeline highlights: - FrostySdk v2 vendored as a submodule under Dependencies/FrostyToolsuite, used for mount / asset enumeration / chunk fetch only. - Custom MeshSet RES parser for the SWBF2 / SWS profile, including subset-category-aware filtering of depth-only / shadow proxies. - BC1-7 / R8 / RGBA8 texture decode, with BC4 / BC5 routed through AssetRipper (Detex's RGTC paths AV on some Squadrons assets). - Frostbite NMA packed-normal channel (R=Nx, G=Ny, B=Metallic, A=AO) is reconstructed into a proper RGB normal map so glTF viewers stop reading Metallic as Normal.Z. - Self-describing EBX V2 walker for MeshVariationDatabase + SSP so material -> texture bindings work without TypeLibrary or per-game SDK. - Per-section UV-channel heuristic (TexCoord0 vs TexCoord1) since FrostyEditor itself uses MeshFallback.hlsl and reads TexCoord0 unconditionally; the heuristic gives a better preview than Frosty does for vehicles that route diffuse through TexCoord1. Known limits documented in the CHANGELOG: skinned meshes are not yet supported; the procedural-glass window slot renders untextured; and meshes whose game shader applies a constant-buffer UV transform (notably the *_dest_ damage-state bodies) preview with mild UV drift. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment on lines
+131
to
+137
| foreach (var hint in _cosmeticSkinHints) | ||
| { | ||
| if (path.Contains(hint, StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| return true; | ||
| } | ||
| } |
Comment on lines
+158
to
+164
| foreach (var t in header.TypeDescriptors) | ||
| { | ||
| if (t.Name == name) | ||
| { | ||
| return t; | ||
| } | ||
| } |
Comment on lines
+114
to
+120
| foreach (var t in header.TypeDescriptors) | ||
| { | ||
| if (t.Name == name) | ||
| { | ||
| return t; | ||
| } | ||
| } |
Comment on lines
+73
to
+76
| catch (Exception ex) | ||
| { | ||
| PerfTimer.Log("frostbite.mount", $"Detex init failed: {ex.GetType().Name}: {ex.Message}"); | ||
| } |
| // on first call; Initialize loads it for the static helper. | ||
| try | ||
| { | ||
| var detexPath = Path.Combine(AppContext.BaseDirectory, DetexHelper.DLL_NAME); |
| if (!ProfilesLibrary.Initialize(profileKey)) | ||
| { | ||
| throw new GMConverterException( | ||
| $"FrostySdk ProfilesLibrary failed to load profile \"{profileKey}\". Expected {profileKey}.json under \"{Path.Combine(AppContext.BaseDirectory, "Profiles")}\"."); |
| return false; | ||
| } | ||
|
|
||
| if (!File.Exists(Path.Combine(installRoot, "Data", "layout.toc"))) |
| { | ||
| foreach (var (exeName, profileKey) in _knownGames) | ||
| { | ||
| if (File.Exists(Path.Combine(installRoot, exeName + ".exe"))) |
| // .frostbiteref is a tiny JSON pointer the FrostbiteImporter reads back. We park each one | ||
| // in a per-resolve directory under temp so the existing UI cleanup (which deletes the | ||
| // resolved-input directory after preview/conversion) sweeps it without extra wiring. | ||
| var resolveDir = Path.Combine(Path.GetTempPath(), "GMConverter", "FrostbiteResolved", Guid.NewGuid().ToString("N")); |
| Directory.CreateDirectory(resolveDir); | ||
|
|
||
| var safeName = NameHelpers.SanitizeFileName(Path.GetFileName(fileEntry.ArchiveEntryPath)); | ||
| var manifestPath = Path.Combine(resolveDir, $"{safeName}.frostbiteref"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
MeshAsset/CompositeMeshAsset/TextureAssetentries, and preview meshes as glTF with textures resolved throughMeshVariationDatabaseandSurfaceShaderPresetEBX walking.MvdbWalker,SspWalker) — no TypeLibrary / per-game SDK DLL required. BC1–7 / R8 / RGBA8 texture decode, with BC4 / BC5 routed through AssetRipper (Detex's RGTC paths AV on some Squadrons assets). Frostbite NMA packed-normal (R=Nx, G=Ny, B=Metallic, A=AO) is reconstructed into a proper RGB normal map so glTF viewers don't read Metallic as Normal.Z.MeshFallback.hlsland reads TexCoord0 unconditionally. The heuristic actually beats Frosty's preview for vehicles that route diffuse through TexCoord1 (e.g., the A-wing main body).Known limitations (documented in CHANGELOG)
MeshType == Skinnedis phase-2 work.*_dest_damage-state bodies) preview with mild UV drift. The mesh data still converts correctly.Test plan
MeshAsset/CompositeMeshAsset/TextureAssetentries appearFla_Emp_Dash_CarbonizedMynock_01_mesh— diffuse + normal applied, no UV driftVeh_Reb_Hunt_AWing_mesh— body wearsT_Veh_Reb_Hunt_AWing_01_CS, normals correct, glass is untextured (expected)dotnet build GMConverter.slnx --configuration Release --no-restorepassesdotnet format GMConverter.slnx --verify-no-changes --severity warn --no-restore --exclude Dependencies/passes🤖 Generated with Claude Code