Exemple #1
0
        public CSkeletalMesh(USkeletalMesh mesh)
        {
            OriginalMesh = mesh;

            // convert bounds
            BoundingSphere = new FSphere
            {
                R = mesh.Bounds.sphere_radius / 2
            };
            BoundingBox = new FBox
            {
                Min = mesh.Bounds.origin - mesh.Bounds.box_extend,
                Max = mesh.Bounds.origin + mesh.Bounds.box_extend
            };

            // MeshScale, MeshOrigin, RotOrigin are removed in UE4
            //!! NOTE: MeshScale is integrated into RefSkeleton.RefBonePose[0].Scale3D.
            //!! Perhaps rotation/translation are integrated too!
            MeshOrigin = new CVec3 {
                v = new float[] { 0, 0, 0 }
            };
            RotOrigin = new FRotator {
                pitch = 0, roll = 0, yaw = 0
            };
            MeshScale = new CVec3 {
                v = new float[] { 1, 1, 1 }
            };

            // convert LODs
            Lods = new CSkelMeshLod[mesh.LODModels.Length];
            for (int i = 0; i < Lods.Length; i++)
            {
                var SrcLod = mesh.LODModels[i];
                if (SrcLod.Indices.Indices16.Length == 0 && SrcLod.Indices.Indices32.Length == 0)
                {
                    // No indicies in this lod
                    continue;
                }

                int NumTexCoords = SrcLod.NumTexCoords;
                if (NumTexCoords > 8)
                {
                    throw new FileLoadException($"SkeletalMesh has too many ({NumTexCoords}) UV sets");
                }

                CSkelMeshLod Lod = new CSkelMeshLod
                {
                    NumTexCoords = NumTexCoords,
                    HasNormals   = true,
                    HasTangents  = true,
                };

                // get vertex count and determine vertex source
                int VertexCount = SrcLod.VertexBufferGPUSkin.GetVertexCount();

                bool bUseVerticesFromSections = false;
                // if (VertexCount == 0 && SrcLod.Sections.Length > 0 && SrcLod.Sections[0].SoftVertices.Count > 0)
                // above is used for editor assets, but there are no chunks for soft vertices

                Lod.AllocateVerts(VertexCount);

                int chunkIndex       = -1;
                int lastChunkVertex  = -1;
                int chunkVertexIndex = 0;

                ushort[] BoneMap = null;

                for (int j = 0; j < VertexCount; j++)
                {
                    while (j >= lastChunkVertex) // this will fix any issues with empty chunks or sections
                    {
                        // UE4.13+ code: chunk information migrated to sections
                        FSkelMeshSection S = SrcLod.Sections[++chunkIndex];
                        lastChunkVertex  = (int)(S.base_vertex_index + S.num_vertices);
                        BoneMap          = S.bone_map;
                        chunkVertexIndex = 0;
                    }

                    // get vertex from GPU skin
                    FSkelMeshVertexBase V;

                    if (!SrcLod.VertexBufferGPUSkin.bUseFullPrecisionUVs)
                    {
                        FGPUVert4Half V0    = SrcLod.VertexBufferGPUSkin.VertsHalf[j];
                        FMeshUVHalf[] SrcUV = V0.UV;
                        V = new FSkelMeshVertexBase
                        {
                            Infs   = V0.Infs,
                            Normal = V0.Normal,
                            Pos    = V0.Pos
                        };
                        // UV: convert half -> float
                        Lod.Verts[j].UV = (FMeshUVFloat)SrcUV[0];
                        for (int TexCoordIndex = 1; TexCoordIndex < NumTexCoords; TexCoordIndex++)
                        {
                            Lod.ExtraUV[TexCoordIndex - 1][j] = (FMeshUVFloat)SrcUV[TexCoordIndex];
                        }
                    }
                    else
                    {
                        FGPUVert4Float V0    = SrcLod.VertexBufferGPUSkin.VertsFloat[j];
                        FMeshUVFloat[] SrcUV = V0.UV;
                        V = new FSkelMeshVertexBase
                        {
                            Infs   = V0.Infs,
                            Normal = V0.Normal,
                            Pos    = V0.Pos
                        };
                        // UV: convert half -> float
                        Lod.Verts[j].UV = SrcUV[0];
                        for (int TexCoordIndex = 1; TexCoordIndex < NumTexCoords; TexCoordIndex++)
                        {
                            Lod.ExtraUV[TexCoordIndex - 1][j] = SrcUV[TexCoordIndex];
                        }
                    }
                    Lod.Verts[j].Position = V.Pos;
                    Lod.Verts[j].UnpackNormals(V.Normal);

                    // convert influences
                    Lod.Verts[j].Bone = new short[4];
                    int  k2            = 0;
                    uint PackedWeights = 0;
                    for (int k = 0; k < 4; k++)
                    {
                        int  BoneIndex  = V.Infs.bone_index[k];
                        byte BoneWeight = V.Infs.bone_weight[k];
                        if (BoneWeight == 0)
                        {
                            continue;                  // skip this influence (but do not stop the loop!)
                        }
                        PackedWeights        |= (uint)(BoneWeight << (k2 * 8));
                        Lod.Verts[j].Bone[k2] = (short)BoneMap[BoneIndex];
                        k2++;
                    }
                    Lod.Verts[j].PackedWeights = PackedWeights;
                    if (k2 < 4)
                    {
                        Lod.Verts[j].Bone[k2] = -1;         // mark end of list
                    }
                }

                // indices
                Lod.Indices.Initialize(SrcLod.Indices.Indices16, SrcLod.Indices.Indices32);

                // sections
                Lod.Sections = new CMeshSection[SrcLod.Sections.Length];

                FSkeletalMeshLODInfo Info = mesh.LODInfo[i];

                for (int j = 0; j < SrcLod.Sections.Length; j++)
                {
                    FSkelMeshSection S   = SrcLod.Sections[j];
                    CMeshSection     Dst = new CMeshSection();

                    // remap material for LOD
                    int MaterialIndex = S.material_index;
                    if (MaterialIndex >= 0 && MaterialIndex < Info.LODMaterialMap.Length)
                    {
                        MaterialIndex = Info.LODMaterialMap[MaterialIndex];
                    }

                    if (S.material_index < mesh.Materials.Length)
                    {
                        Dst.Material = new UUnrealMaterial();// mesh.Materials[MaterialIndex].Material;
                    }
                    // -> TODO: actually get the object from the pak

                    Dst.FirstIndex  = (int)S.base_index;
                    Dst.NumFaces    = (int)S.num_triangles;
                    Lod.Sections[j] = Dst;
                }

                Lods[i] = Lod;
            }

            // copy skeleton
            int NumBones = mesh.RefSkeleton.ref_bone_info.Length;

            RefSkeleton = new CSkelMeshBone[NumBones];
            for (int i = 0; i < NumBones; i++)
            {
                FMeshBoneInfo B   = mesh.RefSkeleton.ref_bone_info[i];
                FTransform    T   = mesh.RefSkeleton.ref_bone_pose[i];
                CSkelMeshBone Dst = new CSkelMeshBone
                {
                    Name        = B.name,
                    ParentIndex = B.parent_index,
                    Position    = T.translation,
                    Orientation = T.rotation
                };
                // fix skeleton; all bones but 0
                if (i >= 1)
                {
                    Dst.Orientation.Conjugate();
                }
                RefSkeleton[i] = Dst;
            }

            Sockets = null; // dunno where this is set

            FinalizeMesh();
        }
Exemple #2
0
        public void SortBones()
        {
            int NumBones = RefSkeleton.Length;
            int i;

            // prepare CBoneSortHelper
            if (NumBones >= 1024 * 3)
            {
                throw new FileLoadException("Too many bones");
            }

            CBoneSortHelper helper = new CBoneSortHelper();

            for (i = 0; i < NumBones; i++)
            {
                helper.Bones[i] = new CBoneSortHelper.Proxy
                {
                    Bone   = RefSkeleton[i],
                    Parent = (i > 0) ? helper.Bones[RefSkeleton[i].ParentIndex] : null
                };
                helper.SortedBones[i] = helper.Bones[i];
            }

            // sort bones
            helper.SortBoneArray(NumBones);

            // build remap table
            int[] Remap     = new int[1024 * 3];
            int[] RemapBack = new int[1024 * 3];
            for (i = 0; i < NumBones; i++)
            {
                CBoneSortHelper.Proxy P = helper.SortedBones[i];
                int OldIndex            = Array.IndexOf(helper.Bones, P);
                Remap[OldIndex] = i;
                RemapBack[i]    = OldIndex;
            }

            // build new RefSkeleton
            CSkelMeshBone[] NewSkeleton = new CSkelMeshBone[NumBones];
            for (i = 0; i < NumBones; i++)
            {
                NewSkeleton[i] = RefSkeleton[RemapBack[i]];
                int oldParent = NewSkeleton[i].ParentIndex;
                NewSkeleton[i].ParentIndex = (oldParent > 0) ? Remap[oldParent] : 0;
            }
            Array.Copy(NewSkeleton, RefSkeleton, NumBones);

            // remap bone influences
            for (int lod = 0; lod < Lods.Length; lod++)
            {
                CSkelMeshLod L = Lods[lod];
                int          V = 0;
                for (i = 0; i < L.NumVerts; i++, V++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        int Bone = L.Verts[V].Bone[j];
                        if (Bone < 0)
                        {
                            break;
                        }
                        L.Verts[V].Bone[j] = (short)Remap[Bone];
                    }
                }
            }
        }
Exemple #3
0
        public static void ExportMesh(BinaryWriter writer, CSkeletalMesh Mesh, CSkelMeshLod Lod)
        {
            VChunkHeader BoneHdr, InfHdr;

            int          i, j;
            CVertexShare Share = new CVertexShare();

            // weld vertices
            // The code below differs from similar code for StaticMesh export: it relies on vertex weight
            // information to not perform occasional welding of vertices which has the same position and
            // normal, but belongs to different bones.
            Share.Prepare(Lod.Verts, Lod.NumVerts, 48);
            for (i = 0; i < Lod.NumVerts; i++)
            {
                CSkelMeshVertex S = Lod.Verts[i];
                // Here we relies on high possibility that vertices which should be shared between
                // triangles will have the same order of weights and bones (because most likely
                // these vertices were duplicated by copying). Doing more complicated comparison
                // will reduce performance with possibly reducing size of exported mesh by a few
                // more vertices.
                uint WeightsHash = S.PackedWeights;
                for (j = 0; j < S.Bone.Length; j++)
                {
                    WeightsHash ^= (uint)(S.Bone[j] << j);
                }
                Share.AddVertex(S.Position, S.Normal, WeightsHash);
            }

            ExportCommonMeshData(writer, Lod.Sections, Lod.Verts, Lod.Indices, Share);

            int numBones = Mesh.RefSkeleton.Length;

            BoneHdr = new VChunkHeader
            {
                DataCount = numBones,
                DataSize  = 120
            };
            SAVE_CHUNK(writer, BoneHdr, "REFSKELT");
            for (i = 0; i < numBones; i++)
            {
                VBone B = new VBone {
                    Name = new byte[64]
                };
                CSkelMeshBone S = Mesh.RefSkeleton[i];
                Extensions.StrCpy(B.Name, S.Name);
                // count NumChildren
                int NumChildren = 0;
                for (j = 0; j < numBones; j++)
                {
                    if ((j != i) && (Mesh.RefSkeleton[j].ParentIndex == i))
                    {
                        NumChildren++;
                    }
                }
                B.NumChildren         = NumChildren;
                B.ParentIndex         = S.ParentIndex;
                B.BonePos.Position    = S.Position;
                B.BonePos.Orientation = S.Orientation;

                B.BonePos.Orientation.Y *= -1;
                B.BonePos.Orientation.W *= -1;
                B.BonePos.Position.Y    *= -1;

                B.Write(writer);
            }

            // count influences
            int NumInfluences = 0;

            for (i = 0; i < Share.Points.Count; i++)
            {
                int             WedgeIndex = Share.VertToWedge[i];
                CSkelMeshVertex V          = Lod.Verts[WedgeIndex];
                for (j = 0; j < 4; j++)
                {
                    if (V.Bone[j] < 0)
                    {
                        break;
                    }
                    NumInfluences++;
                }
            }

            // write influences
            InfHdr = new VChunkHeader
            {
                DataCount = NumInfluences,
                DataSize  = 12
            };
            SAVE_CHUNK(writer, InfHdr, "RAWWEIGHTS");
            for (i = 0; i < Share.Points.Count; i++)
            {
                int             WedgeIndex      = Share.VertToWedge[i];
                CSkelMeshVertex V               = Lod.Verts[WedgeIndex];
                CVec4           UnpackedWeights = V.UnpackWeights();
                for (j = 0; j < 4; j++)
                {
                    if (V.Bone[j] < 0)
                    {
                        break;
                    }
                    NumInfluences--;                // just for verification

                    VRawBoneInfluence I;
                    I.Weight     = UnpackedWeights.v[j];
                    I.BoneIndex  = V.Bone[j];
                    I.PointIndex = i;
                    I.Write(writer);
                }
            }
            if (NumInfluences != 0)
            {
                throw new FileLoadException("Did not write to all influences");
            }

            ExportExtraUV(writer, Lod.ExtraUV, Lod.NumVerts, Lod.NumTexCoords);
        }