예제 #1
0
        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);
        }
예제 #2
0
        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>());
            }
        }
예제 #3
0
 public void Serialize(FCustomArchiveWriter writer)
 {
     Orientation.Serialize(writer);
     Position.Serialize(writer);
     writer.Write(Length);
     Size.Serialize(writer);
 }
예제 #4
0
        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);
        }
예제 #5
0
        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));
                }
예제 #6
0
        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);
        }