private void SerializeStreamedData(FArchive Ar, bool bHasVertexColors) { var stripDataFlags = Ar.Read <FStripDataFlags>(); Indices = new FMultisizeIndexContainer(Ar); VertexBufferGPUSkin = new FSkeletalMeshVertexBuffer { bUseFullPrecisionUVs = true }; var positionVertexBuffer = new FPositionVertexBuffer(Ar); var staticMeshVertexBuffer = new FStaticMeshVertexBuffer(Ar); var skinWeightVertexBuffer = new FSkinWeightVertexBuffer(Ar, VertexBufferGPUSkin.bExtraBoneInfluences); if (bHasVertexColors) { var newColorVertexBuffer = new FColorVertexBuffer(Ar); ColorVertexBuffer = new FSkeletalMeshVertexColorBuffer(newColorVertexBuffer.Data); } if (FUE5ReleaseStreamObjectVersion.Get(Ar) < FUE5ReleaseStreamObjectVersion.Type.RemovingTessellation && !stripDataFlags.IsClassDataStripped((byte)EClassDataStripFlag.CDSF_AdjacencyData)) { AdjacencyIndexBuffer = new FMultisizeIndexContainer(Ar); } if (HasClothData()) { ClothVertexBuffer = new FSkeletalMeshVertexClothBuffer(Ar); } var skinWeightProfilesData = new FSkinWeightProfilesData(Ar); if (Ar.Game >= EGame.GAME_UE5_0) // Note: This was added in UE4.27, but we're only reading it on UE5 for compatibility with Fortnite { var rayTracingData = Ar.ReadArray <byte>(); } NumVertices = positionVertexBuffer.NumVertices; NumTexCoords = staticMeshVertexBuffer.NumTexCoords; VertexBufferGPUSkin.VertsFloat = new FGPUVertFloat[NumVertices]; for (var i = 0; i < VertexBufferGPUSkin.VertsFloat.Length; i++) { VertexBufferGPUSkin.VertsFloat[i] = new FGPUVertFloat { Pos = positionVertexBuffer.Verts[i], Infs = skinWeightVertexBuffer.Weights[i], Normal = staticMeshVertexBuffer.UV[i].Normal, UV = staticMeshVertexBuffer.UV[i].UV }; } }
public FStaticLODModel(FAssetArchive Ar, bool bHasVertexColors) : this() { if (Ar.Game == EGame.GAME_SEAOFTHIEVES) { Ar.Position += 4; } var stripDataFlags = Ar.Read <FStripDataFlags>(); var skelMeshVer = FSkeletalMeshCustomVersion.Get(Ar); if (Ar.Game == EGame.GAME_SEAOFTHIEVES) { Ar.Position += 4; } Sections = Ar.ReadArray(Ar.Read <int>(), () => new FSkelMeshSection(Ar)); if (skelMeshVer < FSkeletalMeshCustomVersion.Type.SplitModelAndRenderData) { Indices = new FMultisizeIndexContainer(Ar); } else { // UE4.19+ uses 32-bit index buffer (for editor data) Indices = new FMultisizeIndexContainer { Indices32 = Ar.ReadBulkArray <uint>() }; } ActiveBoneIndices = Ar.ReadArray <short>(); if (skelMeshVer < FSkeletalMeshCustomVersion.Type.CombineSectionWithChunk) { Chunks = Ar.ReadArray(() => new FSkelMeshChunk(Ar)); } Size = Ar.Read <int>(); if (!stripDataFlags.IsDataStrippedForServer()) { NumVertices = Ar.Read <int>(); } RequiredBones = Ar.ReadArray <short>(); if (!stripDataFlags.IsEditorDataStripped()) { RawPointIndices = new FIntBulkData(Ar); } if (Ar.Game != EGame.GAME_SOD2 && Ar.Ver >= UE4Version.VER_UE4_ADD_SKELMESH_MESHTOIMPORTVERTEXMAP) { MeshToImportVertexMap = Ar.ReadArray <int>(); MaxImportVertex = Ar.Read <int>(); } if (!stripDataFlags.IsDataStrippedForServer()) { NumTexCoords = Ar.Read <int>(); if (skelMeshVer < FSkeletalMeshCustomVersion.Type.SplitModelAndRenderData) { VertexBufferGPUSkin = new FSkeletalMeshVertexBuffer(Ar); if (skelMeshVer >= FSkeletalMeshCustomVersion.Type.UseSeparateSkinWeightBuffer) { var skinWeights = new FSkinWeightVertexBuffer(Ar, VertexBufferGPUSkin.bExtraBoneInfluences); if (skinWeights.Weights.Length > 0) { // Copy data to VertexBufferGPUSkin if (VertexBufferGPUSkin.bUseFullPrecisionUVs) { for (var i = 0; i < NumVertices; i++) { VertexBufferGPUSkin.VertsFloat[i].Infs = skinWeights.Weights[i]; } } else { for (var i = 0; i < NumVertices; i++) { VertexBufferGPUSkin.VertsHalf[i].Infs = skinWeights.Weights[i]; } } } } if (bHasVertexColors) { if (skelMeshVer < FSkeletalMeshCustomVersion.Type.UseSharedColorBufferFormat) { ColorVertexBuffer = new FSkeletalMeshVertexColorBuffer(Ar); } else { var newColorVertexBuffer = new FColorVertexBuffer(Ar); ColorVertexBuffer = new FSkeletalMeshVertexColorBuffer(newColorVertexBuffer.Data); } } if (Ar.Ver < UE4Version.VER_UE4_REMOVE_EXTRA_SKELMESH_VERTEX_INFLUENCES) { throw new ParserException("Unsupported: extra SkelMesh vertex influences (old mesh format)"); } // https://github.com/gildor2/UEViewer/blob/master/Unreal/UnrealMesh/UnMesh4.cpp#L1415 if (Ar.Game == EGame.GAME_SOD2) { Ar.Position += 8; return; } if (Ar.Game == EGame.GAME_SEAOFTHIEVES) { var arraySize = Ar.Read <int>(); Ar.Position += arraySize * 44; for (var i = 0; i < 4; i++) { Ar.ReadArray <int>(); // 4 arrays worth } Ar.Position += 13; } if (!stripDataFlags.IsClassDataStripped((byte)EClassDataStripFlag.CDSF_AdjacencyData)) { AdjacencyIndexBuffer = new FMultisizeIndexContainer(Ar); } if (Ar.Ver >= UE4Version.VER_UE4_APEX_CLOTH && HasClothData()) { ClothVertexBuffer = new FSkeletalMeshVertexClothBuffer(Ar); } } } if (Ar.Game == EGame.GAME_SEAOFTHIEVES) { var _ = new FMultisizeIndexContainer(Ar); } }
public void SerializeRenderItem(FAssetArchive Ar, bool bHasVertexColors, byte numVertexColorChannels) { if (Ar.Game < EGame.GAME_UE4_24) { SerializeRenderItem_Legacy(Ar, bHasVertexColors, numVertexColorChannels); return; } var stripDataFlags = Ar.Read <FStripDataFlags>(); var bIsLODCookedOut = Ar.ReadBoolean(); var bInlined = Ar.ReadBoolean(); RequiredBones = Ar.ReadArray <short>(); if (!stripDataFlags.IsDataStrippedForServer() && !bIsLODCookedOut) { Sections = new FSkelMeshSection[Ar.Read <int>()]; for (var i = 0; i < Sections.Length; i++) { Sections[i] = new FSkelMeshSection(); Sections[i].SerializeRenderItem(Ar); } ActiveBoneIndices = Ar.ReadArray <short>(); Ar.Position += 4; //var buffersSize = Ar.Read<uint>(); if (bInlined) { SerializeStreamedData(Ar, bHasVertexColors); if (Ar.Game == EGame.GAME_ROGUECOMPANY) { Ar.Position += 10; // FStripDataFlags, ElementSize, ElementCount Ar.SkipBulkArrayData(); Ar.Position += 10; } } else { var bulk = new FByteBulkData(Ar); if (bulk.Header.ElementCount > 0) { using (var tempAr = new FByteArchive("LodReader", bulk.Data, Ar.Versions)) { SerializeStreamedData(tempAr, bHasVertexColors); } var skipBytes = 5; if (FUE5ReleaseStreamObjectVersion.Get(Ar) < FUE5ReleaseStreamObjectVersion.Type.RemovingTessellation && !stripDataFlags.IsClassDataStripped((byte)EClassDataStripFlag.CDSF_AdjacencyData)) { skipBytes += 5; } skipBytes += 4 * 4 + 2 * 4 + 2 * 4; skipBytes += FSkinWeightVertexBuffer.MetadataSize(Ar); Ar.Position += skipBytes; if (HasClothData()) { var clothIndexMapping = Ar.ReadArray <long>(); Ar.Position += 2 * 4; } var profileNames = Ar.ReadArray(Ar.ReadFName); } } } }
private void SerializeRenderItem_Legacy(FAssetArchive Ar, bool bHasVertexColors, byte numVertexColorChannels) { var stripDataFlags = Ar.Read <FStripDataFlags>(); Sections = new FSkelMeshSection[Ar.Read <int>()]; for (var i = 0; i < Sections.Length; i++) { Sections[i] = new FSkelMeshSection(); Sections[i].SerializeRenderItem(Ar); } Indices = new FMultisizeIndexContainer(Ar); VertexBufferGPUSkin = new FSkeletalMeshVertexBuffer { bUseFullPrecisionUVs = true }; ActiveBoneIndices = Ar.ReadArray <short>(); RequiredBones = Ar.ReadArray <short>(); if (!stripDataFlags.IsDataStrippedForServer() && !stripDataFlags.IsClassDataStripped((byte)EClassDataStripFlag.CDSF_MinLodData)) { var positionVertexBuffer = new FPositionVertexBuffer(Ar); var staticMeshVertexBuffer = new FStaticMeshVertexBuffer(Ar); var skinWeightVertexBuffer = new FSkinWeightVertexBuffer(Ar, VertexBufferGPUSkin.bExtraBoneInfluences); if (!bHasVertexColors && Ar.Game == EGame.GAME_BORDERLANDS3) { for (var i = 0; i < numVertexColorChannels; i++) { var newColorVertexBuffer = new FColorVertexBuffer(Ar); ColorVertexBuffer = new FSkeletalMeshVertexColorBuffer(newColorVertexBuffer.Data); } } else if (bHasVertexColors) { var newColorVertexBuffer = new FColorVertexBuffer(Ar); ColorVertexBuffer = new FSkeletalMeshVertexColorBuffer(newColorVertexBuffer.Data); } if (!stripDataFlags.IsClassDataStripped((byte)EClassDataStripFlag.CDSF_AdjacencyData)) { AdjacencyIndexBuffer = new FMultisizeIndexContainer(Ar); } if (HasClothData()) { ClothVertexBuffer = new FSkeletalMeshVertexClothBuffer(Ar); } NumVertices = positionVertexBuffer.NumVertices; NumTexCoords = staticMeshVertexBuffer.NumTexCoords; VertexBufferGPUSkin.VertsFloat = new FGPUVertFloat[NumVertices]; for (var i = 0; i < VertexBufferGPUSkin.VertsFloat.Length; i++) { VertexBufferGPUSkin.VertsFloat[i] = new FGPUVertFloat { Pos = positionVertexBuffer.Verts[i], Infs = skinWeightVertexBuffer.Weights[i], Normal = staticMeshVertexBuffer.UV[i].Normal, UV = staticMeshVertexBuffer.UV[i].UV }; } } if (Ar.Game >= EGame.GAME_UE4_23) { var skinWeightProfilesData = new FSkinWeightProfilesData(Ar); } }
// UE ref https://github.com/EpicGames/UnrealEngine/blob/26450a5a59ef65d212cf9ce525615c8bd673f42a/Engine/Source/Runtime/Engine/Private/SkeletalMeshLODRenderData.cpp#L710 public void SerializeRenderItem(FAssetArchive Ar, bool bHasVertexColors, byte numVertexColorChannels) { var stripDataFlags = Ar.Read <FStripDataFlags>(); var bIsLODCookedOut = false; if (Ar.Game != EGame.GAME_Splitgate) { bIsLODCookedOut = Ar.ReadBoolean(); } var bInlined = Ar.ReadBoolean(); RequiredBones = Ar.ReadArray <short>(); if (!stripDataFlags.IsDataStrippedForServer() && !bIsLODCookedOut) { Sections = new FSkelMeshSection[Ar.Read <int>()]; for (var i = 0; i < Sections.Length; i++) { Sections[i] = new FSkelMeshSection(); Sections[i].SerializeRenderItem(Ar); } ActiveBoneIndices = Ar.ReadArray <short>(); if (Ar.Game == EGame.GAME_KenaBridgeofSpirits) { Ar.ReadArray <byte>(); // EAssetType_array1 } Ar.Position += 4; //var buffersSize = Ar.Read<uint>(); if (bInlined) { SerializeStreamedData(Ar, bHasVertexColors); if (Ar.Game == EGame.GAME_RogueCompany) { Ar.Position += 12; // 1 (Long) + 2^16 (Int) var elementSize = Ar.Read <int>(); var elementCount = Ar.Read <int>(); if (elementSize > 0 && elementCount > 0) { Ar.SkipBulkArrayData(); } } } else { var bulk = new FByteBulkData(Ar); if (bulk.Header.ElementCount > 0) { using (var tempAr = new FByteArchive("LodReader", bulk.Data, Ar.Versions)) { SerializeStreamedData(tempAr, bHasVertexColors); } var skipBytes = 5; if (FUE5ReleaseStreamObjectVersion.Get(Ar) < FUE5ReleaseStreamObjectVersion.Type.RemovingTessellation && !stripDataFlags.IsClassDataStripped((byte)EClassDataStripFlag.CDSF_AdjacencyData)) { skipBytes += 5; } skipBytes += 4 * 4 + 2 * 4 + 2 * 4; skipBytes += FSkinWeightVertexBuffer.MetadataSize(Ar); Ar.Position += skipBytes; if (HasClothData()) { var clothIndexMapping = Ar.ReadArray <long>(); Ar.Position += 2 * 4; } var profileNames = Ar.ReadArray(Ar.ReadFName); } } } if (Ar.Game == EGame.GAME_ReadyOrNot) { Ar.Position += 4; } }