Skip to content

UC2 PC static mesh extraction fixes + OBJ export#339

Open
jf1shh wants to merge 5 commits into
gildor2:masterfrom
jf1shh:master
Open

UC2 PC static mesh extraction fixes + OBJ export#339
jf1shh wants to merge 5 commits into
gildor2:masterfrom
jf1shh:master

Conversation

@jf1shh
Copy link
Copy Markdown

@jf1shh jf1shh commented May 22, 2026

UC2 PC static mesh support

Three fixes to extract static meshes from Unreal Championship 2 (PC version, ArVer 140-151):

Fix 1 – Widen UC2 detection range
GameDatabase.cpp: UC2 was only detected at ArVer==151 (Xbox). PC packages use ArVer 140-151, so widened the range to cover
both.

Fix 2 – Split Xbox/PC vertex stream paths
UnMesh2.h: Xbox (ArLicenseeVer=1) has a Flag guard before each vertex array; PC (ArLicenseeVer=0) does not. PC vertex
stream reads FVector (3 floats) per vertex, then 3 ints for the packed normal.

Fix 3 – Fix export table and vertex count for ArVer<145 packages
UnPackage2.cpp / UnMesh2.h: Below ArVer=145, ClassIndex/SuperIndex/SerialSize/SerialOffset and TArray counts use AR_INDEX
compact encoding rather than int32. Matches the FName threshold already in UnPackage.cpp.

Also skips LODs with 0 vertices to avoid crashing on _EM emissive props with no geometry.

Tested against all 45 UC2 PC static mesh packages — 2,093 meshes extract cleanly.


OBJ export for static meshes (-obj flag)

Adds ExportStaticMeshOBJ() producing .obj + .mtl per mesh/LOD, selectable via -obj on the command line or the Static
Mesh format dropdown in Settings. Per-section material groups are named from UUnrealMaterial names. Useful for importing
into UE2-era editors (e.g. UT2004 UnrealEd).

jf1shh and others added 5 commits May 20, 2026 18:34
- Widen UC2 game detection to ArVer 140-151 (was only 151) to cover PC packages
- Split Xbox (ArLicenseeVer=1) and PC (ArLicenseeVer=0) serialization paths in
  FStaticMeshVertexStream, FRawColorStream, and FStaticMeshUVStream: Xbox streams
  are guarded by a Flag field, PC streams store data directly
- On PC, FStaticMeshVertexStream deserializes vertices one at a time to handle the
  packed int[3] format used by FUC2Vector/FUC2Normal
- LoadExternalUC2Data() is now Xbox-only; PC packages store all geometry inline
- Fix FStaticMeshSection::NumFaces on PC: the field holds the last index value, not
  a face count, so divide by 3 after load
- Remove the assert in LoadExternalUC2Data that fired when IndexStream1 was already
  populated (valid on PC)
On PC (ArLicenseeVer==0) the FPackedPosition serializer writes vertex
positions as FVector (3 floats, 12 bytes each) rather than the Xbox
int16 triplet (6 bytes). Normals are stored as 3 ints (12 bytes).

The previous fix iterated over FStaticMeshVertexUC2 elements one by one
which advanced the archive by the right byte count but left V.X/V.Y/V.Z
at zero (local read vars were never stored), causing all vertices to
collapse to a single deduplicated point in the PSK output.

The new PC path bypasses FStaticMeshVertexUC2 entirely and reads
directly into FStaticMeshVertex: FVector for position (world-space
floats, no GUC2VectorScale/Base transform needed) and int[3] for the
normal (consumed but not yet decoded).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ArVer=140 packages (6 out of 45 UC2 PC static mesh packages) use
compact AR_INDEX encoding for export table fields that later versions
store as full int32. This matches the FName serialization version
threshold already in UnPackage.cpp (ArVer >= 145 uses int32).

- UnPackage2.cpp: Serialize2X() now branches on ArVer<145:
  use AR_INDEX for ClassIndex, SuperIndex, SerialSize, SerialOffset
  (ArVer>=145 path unchanged - still uses full int32 with int16 truncation)
- UnMesh2.h: UC2 PC vertex stream count also stored as AR_INDEX for
  ArVer<145; use AR_INDEX(Count) when reading vertex count in that path

Before: 6 packages failed with "wrong name index" errors because
misaligned export table reads produced OOB name indices.
After: all 45 UC2 PC static mesh packages export cleanly (2,095 files).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ExportStaticMeshOBJ() producing .obj + .mtl per mesh LOD, selected
via -obj on the command line or the Settings dialog. Per-section material
groups are named from UUnrealMaterial names. Targeting UT2004 mod import
(raw UE coordinate space, no axis transform).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 3 ints following each vertex position are float bits, not integer
normals. Reinterpret via bitcast. Previously set to (0,0,0).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant