private void ExportStaticMeshLods(CStaticMeshLod lod, FArchiveWriter Ar, List <MaterialExporter>?materialExports, ETexturePlatform platform = ETexturePlatform.DesktopMobile) { var share = new CVertexShare(); var boneHdr = new VChunkHeader(); var infHdr = new VChunkHeader(); share.Prepare(lod.Verts); foreach (var vert in lod.Verts) { share.AddVertex(vert.Position, vert.Normal); } ExportCommonMeshData(Ar, lod.Sections.Value, lod.Verts, lod.Indices.Value, share, materialExports, platform); boneHdr.DataCount = 0; boneHdr.DataSize = 120; Ar.SerializeChunkHeader(boneHdr, "REFSKELT"); infHdr.DataCount = 0; infHdr.DataSize = 12; Ar.SerializeChunkHeader(infHdr, "RAWWEIGHTS"); ExportVertexColors(Ar, lod.VertexColors, lod.NumVerts); ExportExtraUV(Ar, lod.ExtraUV.Value, lod.NumVerts, lod.NumTexCoords); }
private void ExportSkeletalMeshLod(CSkelMeshLod lod, List <CSkelMeshBone> bones, FArchiveWriter Ar, List <MaterialExporter>?materialExports, ETexturePlatform platform = ETexturePlatform.DesktopMobile) { var share = new CVertexShare(); var infHdr = new VChunkHeader(); share.Prepare(lod.Verts); foreach (var vert in lod.Verts) { var weightsHash = vert.PackedWeights; for (var i = 0; i < vert.Bone.Length; i++) { weightsHash ^= (uint)vert.Bone[i] << i; } share.AddVertex(vert.Position, vert.Normal, weightsHash); } ExportCommonMeshData(Ar, lod.Sections.Value, lod.Verts, lod.Indices.Value, share, materialExports, platform); ExportSkeletonData(Ar, bones); var numInfluences = 0; for (var i = 0; i < share.Points.Count; i++) { for (var j = 0; j < Constants.NUM_INFLUENCES_UE4; j++) { if (lod.Verts[share.VertToWedge.Value[i]].Bone[j] < 0) { break; } numInfluences++; } } infHdr.DataCount = numInfluences; infHdr.DataSize = 12; Ar.SerializeChunkHeader(infHdr, "RAWWEIGHTS"); for (var i = 0; i < share.Points.Count; i++) { var v = lod.Verts[share.VertToWedge.Value[i]]; var unpackedWeights = v.UnpackWeights(); for (var j = 0; j < Constants.NUM_INFLUENCES_UE4; j++) { if (v.Bone[j] < 0) { break; } Ar.Write(unpackedWeights[j]); Ar.Write(i); Ar.Write((int)v.Bone[j]); } } ExportVertexColors(Ar, lod.VertexColors, lod.NumVerts); ExportExtraUV(Ar, lod.ExtraUV.Value, lod.NumVerts, lod.NumTexCoords); }
public MeshExporter(USkeleton originalSkeleton) { MeshLods = new List <Mesh>(); MeshName = originalSkeleton.Owner?.Name ?? originalSkeleton.Name; if (!originalSkeleton.TryConvert(out var bones) || bones.Count == 0) { Log.Logger.Warning($"Skeleton '{MeshName}' has no bone"); return; } using var Ar = new FArchiveWriter(); var mainHdr = new VChunkHeader { TypeFlag = Constants.PSK_VERSION }; Ar.SerializeChunkHeader(mainHdr, "ACTRHEAD"); ExportSkeletonData(Ar, bones); MeshLods.Add(new Mesh($"{MeshName}.psk", Ar.GetBuffer(), new List <MaterialExporter>())); }
private void ExportCommonMeshData(FArchiveWriter Ar, CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, CVertexShare share, List <MaterialExporter>?materialExports, ETexturePlatform platform = ETexturePlatform.DesktopMobile) { var mainHdr = new VChunkHeader(); var ptsHdr = new VChunkHeader(); var wedgHdr = new VChunkHeader(); var facesHdr = new VChunkHeader(); var matrHdr = new VChunkHeader(); var normHdr = new VChunkHeader(); mainHdr.TypeFlag = Constants.PSK_VERSION; Ar.SerializeChunkHeader(mainHdr, "ACTRHEAD"); var numPoints = share.Points.Count; ptsHdr.DataCount = numPoints; ptsHdr.DataSize = 12; Ar.SerializeChunkHeader(ptsHdr, "PNTS0000"); for (var i = 0; i < numPoints; i++) { var point = share.Points[i]; point.Y = -point.Y; // MIRROR_MESH point.Serialize(Ar); } var numFaces = 0; var numVerts = verts.Length; var numSections = sections.Length; var wedgeMat = new int[numVerts]; for (var i = 0; i < numSections; i++) { var faces = sections[i].NumFaces; numFaces += faces; for (var j = 0; j < faces * 3; j++) { wedgeMat[indices[j + sections[i].FirstIndex]] = i; } } wedgHdr.DataCount = numVerts; wedgHdr.DataSize = 16; Ar.SerializeChunkHeader(wedgHdr, "VTXW0000"); for (var i = 0; i < numVerts; i++) { Ar.Write(share.WedgeToVert[i]); Ar.Write(verts[i].UV.U); Ar.Write(verts[i].UV.V); Ar.Write((byte)wedgeMat[i]); Ar.Write((byte)0); Ar.Write((short)0); } facesHdr.DataCount = numFaces; if (numVerts <= 65536) { facesHdr.DataSize = 12; Ar.SerializeChunkHeader(facesHdr, "FACE0000"); for (var i = 0; i < numSections; i++) { for (var j = 0; j < sections[i].NumFaces; j++) { var wedgeIndex = new ushort[3]; for (var k = 0; k < wedgeIndex.Length; k++) { wedgeIndex[k] = (ushort)indices[sections[i].FirstIndex + j * 3 + k]; } Ar.Write(wedgeIndex[1]); // MIRROR_MESH Ar.Write(wedgeIndex[0]); // MIRROR_MESH Ar.Write(wedgeIndex[2]); Ar.Write((byte)i); Ar.Write((byte)0); Ar.Write((uint)1); } } } else { facesHdr.DataSize = 18; Ar.SerializeChunkHeader(facesHdr, "FACE3200"); for (var i = 0; i < numSections; i++) { for (var j = 0; j < sections[i].NumFaces; j++) { var wedgeIndex = new int[3]; for (var k = 0; k < wedgeIndex.Length; k++) { wedgeIndex[k] = indices[sections[i].FirstIndex + j * 3 + k]; } Ar.Write(wedgeIndex[1]); // MIRROR_MESH Ar.Write(wedgeIndex[0]); // MIRROR_MESH Ar.Write(wedgeIndex[2]); Ar.Write((byte)i); Ar.Write((byte)0); Ar.Write((uint)1); } } } matrHdr.DataCount = numSections; matrHdr.DataSize = 88; Ar.SerializeChunkHeader(matrHdr, "MATT0000"); for (var i = 0; i < numSections; i++) { string materialName; if (sections[i].Material?.Load <UMaterialInterface>() is { } tex) { materialName = tex.Name; materialExports?.Add(new MaterialExporter(tex, true, platform)); }