private void ExportStaticMeshLods(CStaticMeshLod lod, FCustomArchiveWriter writer, List <MaterialExporter>?materialExports) { 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(writer, lod.Sections.Value, lod.Verts, lod.Indices.Value, share, materialExports); boneHdr.DataCount = 0; boneHdr.DataSize = 120; writer.SerializeChunkHeader(boneHdr, "REFSKELT"); infHdr.DataCount = 0; infHdr.DataSize = 12; writer.SerializeChunkHeader(infHdr, "RAWWEIGHTS"); ExportVertexColors(writer, lod.VertexColors, lod.NumVerts); ExportExtraUV(writer, lod.ExtraUV.Value, lod.NumVerts, lod.NumTexCoords); }
public MeshExporter(USkeletalMesh originalMesh, ELodFormat lodFormat = ELodFormat.FirstLod, bool exportMaterials = true) { _meshName = originalMesh.Owner?.Name ?? originalMesh.Name; if (!originalMesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Length <= 0) { Log.Logger.Warning($"Mesh '{_meshName}' has no LODs"); _meshLods = new Mesh[0]; return; } _meshLods = new Mesh[lodFormat == ELodFormat.AllLods ? convertedMesh.LODs.Length : 1]; for (var i = 0; i < _meshLods.Length; i++) { if (convertedMesh.LODs[i].Sections.Value.Length <= 0 || convertedMesh.LODs[i].Indices.Value == null) { Log.Logger.Warning($"LOD {i} in mesh '{_meshName}' has no section"); continue; } var usePskx = convertedMesh.LODs[i].NumVerts > 65536; using var writer = new FCustomArchiveWriter(); var materialExports = exportMaterials ? new List <MaterialExporter>() : null; ExportSkeletalMeshLod(convertedMesh.LODs[i], convertedMesh.RefSkeleton, writer, materialExports); _meshLods[i] = new Mesh($"{_meshName}_LOD{i}.psk{(usePskx ? 'x' : "")}", writer.GetBuffer(), materialExports ?? new List <MaterialExporter>()); } }
public void Serialize(FCustomArchiveWriter writer) { Orientation.Serialize(writer); Position.Serialize(writer); writer.Write(Length); Size.Serialize(writer); }
public void Serialize(FCustomArchiveWriter writer) { var boneName = new byte[64]; var bone = Encoding.UTF8.GetBytes(Name); Buffer.BlockCopy(bone, 0, boneName, 0, bone.Length); writer.Write(boneName); writer.Write(Flags); writer.Write(NumChildren); writer.Write(ParentIndex); BonePos.Serialize(writer); }
private void ExportCommonMeshData(FCustomArchiveWriter writer, CMeshSection[] sections, CMeshVertex[] verts, FRawStaticIndexBuffer indices, CVertexShare share, List <MaterialExporter>?materialExports) { var mainHdr = new VChunkHeader(); var ptsHdr = new VChunkHeader(); var wedgHdr = new VChunkHeader(); var facesHdr = new VChunkHeader(); var matrHdr = new VChunkHeader(); mainHdr.TypeFlag = _PSK_VERSION; writer.SerializeChunkHeader(mainHdr, "ACTRHEAD"); var numPoints = share.Points.Count; ptsHdr.DataCount = numPoints; ptsHdr.DataSize = 12; writer.SerializeChunkHeader(ptsHdr, "PNTS0000"); for (var i = 0; i < numPoints; i++) { var point = share.Points[i]; point.Y = -point.Y; // MIRROR_MESH point.Serialize(writer); } 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; writer.SerializeChunkHeader(wedgHdr, "VTXW0000"); for (var i = 0; i < numVerts; i++) { writer.Write(share.WedgeToVert[i]); writer.Write((int)verts[i].UV.U); // the 4 bit int value is the actual needed float value writer.Write((int)verts[i].UV.V); // the 4 bit int value is the actual needed float value writer.Write((byte)wedgeMat[i]); writer.Write((byte)0); writer.Write((short)0); } facesHdr.DataCount = numFaces; if (numVerts <= 65536) { facesHdr.DataSize = 12; writer.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]; } writer.Write(wedgeIndex[1]); // MIRROR_MESH writer.Write(wedgeIndex[0]); // MIRROR_MESH writer.Write(wedgeIndex[2]); writer.Write((byte)i); writer.Write((byte)0); writer.Write((uint)1); } } } else { facesHdr.DataSize = 18; writer.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]; } writer.Write(wedgeIndex[1]); // MIRROR_MESH writer.Write(wedgeIndex[0]); // MIRROR_MESH writer.Write(wedgeIndex[2]); writer.Write((byte)i); writer.Write((byte)0); writer.Write((uint)1); } } } matrHdr.DataCount = numSections; matrHdr.DataSize = 88; writer.SerializeChunkHeader(matrHdr, "MATT0000"); for (var i = 0; i < numSections; i++) { string materialName; if (sections[i].Material?.Value is { } tex) { materialName = tex.Name; materialExports?.Add(new MaterialExporter(tex, true)); }
private void ExportSkeletalMeshLod(CSkelMeshLod lod, CSkelMeshBone[] bones, FCustomArchiveWriter writer, List <MaterialExporter>?materialExports) { var share = new CVertexShare(); var boneHdr = new VChunkHeader(); 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(writer, lod.Sections.Value, lod.Verts, lod.Indices.Value, share, materialExports); var numBones = bones.Length; boneHdr.DataCount = numBones; boneHdr.DataSize = 120; writer.SerializeChunkHeader(boneHdr, "REFSKELT"); for (var i = 0; i < numBones; i++) { var numChildren = 0; for (var j = 0; j < numBones; j++) { if (j != i && bones[j].ParentIndex == i) { numChildren++; } } var bone = new VBone { Name = bones[i].Name.Text, NumChildren = numChildren, ParentIndex = bones[i].ParentIndex, BonePos = new VJointPosPsk { Position = bones[i].Position, Orientation = bones[i].Orientation } }; // MIRROR_MESH bone.BonePos.Orientation.Y *= -1; bone.BonePos.Orientation.W *= -1; bone.BonePos.Position.Y *= -1; bone.Serialize(writer); } var numInfluences = 0; for (var i = 0; i < share.Points.Count; i++) { for (var j = 0; j < 4; j++) { if (lod.Verts[share.VertToWedge.Value[i]].Bone[j] < 0) { break; } numInfluences++; } } infHdr.DataCount = numInfluences; infHdr.DataSize = 12; writer.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 < 4; j++) { if (v.Bone[j] < 0) { break; } writer.Write(unpackedWeights[j]); writer.Write(i); writer.Write((int)v.Bone[j]); } } ExportVertexColors(writer, lod.VertexColors, lod.NumVerts); ExportExtraUV(writer, lod.ExtraUV.Value, lod.NumVerts, lod.NumTexCoords); }