Esempio n. 1
0
 public ModelInterface(USkeletalMesh mesh, int w, int h, Func <string, PakPackage> packageFunc)
 {
     this.w           = w;
     this.h           = h;
     this.mesh        = mesh;
     this.packageFunc = packageFunc;
 }
Esempio n. 2
0
        internal UMeshSection(CSkeletalMesh mesh, USkeletalMesh umesh, int section, int[] disableOverrides, ModelInterface window, Func <string, PakPackage> packageFunc)
        {
            var cLod = mesh.Lods[LOD_LEVEL];
            var uLod = umesh.LODModels[LOD_LEVEL];
            var sec  = uLod.Sections[section];

            if (sec.disabled || Array.IndexOf(disableOverrides, sec.material_index) != -1)
            {
                Enabled = false;
                return;
            }

            texCoords = new Vector2[sec.num_vertices];
            colorData = new Vector3[sec.num_vertices];
            verts     = new Vector3[sec.num_vertices];
            normals   = new Vector3[sec.num_vertices];
            inds      = new int[sec.num_triangles * 3];
            int i;

            for (i = 0; i < sec.num_vertices; i++)
            {
                var v    = cLod.Verts[i + sec.base_vertex_index];
                var posV = v.Position.v;
                var norm = (FVector)(FPackedNormal)v.Normal;
                texCoords[i] = new Vector2(v.UV.U, 1 - v.UV.V);
                verts[i]     = new Vector3(posV[0], posV[1], posV[2]);
                normals[i]   = new Vector3(norm.X, norm.Y, norm.Z);
            }
            for (i = 0; i < sec.num_triangles * 3; i++)
            {
                inds[i] = (int)(cLod.Indices[(int)(i + sec.base_index)] - sec.base_vertex_index);
            }
            Material  = GetMaterial(umesh.MaterialAssets[sec.material_index], window, packageFunc);
            TextureID = Material.DiffuseMap;
        }
Esempio n. 3
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>());
            }
        }
Esempio n. 4
0
        public MeshExporter(USkeletalMesh originalMesh, ELodFormat lodFormat = ELodFormat.FirstLod, bool exportMaterials = true, EMeshFormat meshFormat = EMeshFormat.ActorX, ETexturePlatform platform = ETexturePlatform.DesktopMobile)
        {
            MeshLods = new List <Mesh>();
            MeshName = originalMesh.Owner?.Name ?? originalMesh.Name;

            if (!originalMesh.TryConvert(out var convertedMesh) || convertedMesh.LODs.Count == 0)
            {
                Log.Logger.Warning($"Mesh '{MeshName}' has no LODs");
                return;
            }

            var i = 0;

            foreach (var lod in convertedMesh.LODs)
            {
                if (lod.SkipLod)
                {
                    Log.Logger.Warning($"LOD {i} in mesh '{MeshName}' should be skipped");
                    continue;
                }

                using var Ar = new FArchiveWriter();
                var materialExports = exportMaterials ? new List <MaterialExporter>() : null;
                var ext             = "";
                switch (meshFormat)
                {
                case EMeshFormat.ActorX:
                    ext = convertedMesh.LODs[i].NumVerts > 65536 ? "pskx" : "psk";
                    ExportSkeletalMeshLod(lod, convertedMesh.RefSkeleton, Ar, materialExports, platform);
                    break;

                case EMeshFormat.Gltf2:
                    ext = "glb";
                    new Gltf(MeshName.SubstringAfterLast("/"), lod, convertedMesh.RefSkeleton, materialExports).Save(meshFormat, Ar);
                    break;

                case EMeshFormat.OBJ:
                    ext = "obj";
                    new Gltf(MeshName.SubstringAfterLast("/"), lod, convertedMesh.RefSkeleton, materialExports).Save(meshFormat, Ar);
                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(meshFormat), meshFormat, null);
                }

                MeshLods.Add(new Mesh($"{MeshName}_LOD{i}.{ext}", Ar.GetBuffer(), materialExports ?? new List <MaterialExporter>()));
                if (lodFormat == ELodFormat.FirstLod)
                {
                    break;
                }
                i++;
            }
        }
Esempio n. 5
0
        public static bool TryConvert(this USkeletalMesh originalMesh, out CSkeletalMesh convertedMesh)
        {
            convertedMesh = new CSkeletalMesh();
            if (originalMesh.LODModels == null)
            {
                return(false);
            }

            convertedMesh.BoundingShere = new FSphere(0f, 0f, 0f, originalMesh.ImportedBounds.SphereRadius / 2);
            convertedMesh.BoundingBox   = new FBox(
                originalMesh.ImportedBounds.Origin - originalMesh.ImportedBounds.BoxExtent,
                originalMesh.ImportedBounds.Origin + originalMesh.ImportedBounds.BoxExtent);

            var numLods = originalMesh.LODModels.Length;

            convertedMesh.LODs = new CSkelMeshLod[numLods];
            for (var i = 0; i < convertedMesh.LODs.Length; i++)
            {
                if (originalMesh.LODModels[i] is not
                {
                    Indices: not null
                } srcLod)
                {
                    continue;
                }

                if (srcLod.Indices.Indices16.Length == 0 && srcLod.Indices.Indices32.Length == 0)
                {
                    Log.Logger.Debug($"LOD {i} has no indices, skipping...");
                    continue;
                }

                var numTexCoords = srcLod.NumTexCoords;
                if (numTexCoords > _MAX_MESH_UV_SETS)
                {
                    throw new ParserException($"Skeletal mesh has too many UV sets ({numTexCoords})");
                }

                convertedMesh.LODs[i] = new CSkelMeshLod
                {
                    NumTexCoords = numTexCoords,
                    HasNormals   = true,
                    HasTangents  = true,
                    Indices      = new Lazy <FRawStaticIndexBuffer>(() => new FRawStaticIndexBuffer
                    {
                        Indices16 = srcLod.Indices.Indices16, Indices32 = srcLod.Indices.Indices32
                    }),
                    Sections = new Lazy <CMeshSection[]>(() =>
                    {
                        var sections = new CMeshSection[srcLod.Sections.Length];
                        for (var j = 0; j < sections.Length; j++)
                        {
                            int materialIndex = srcLod.Sections[j].MaterialIndex;
                            if (materialIndex < 0)      // UE4 using Clamp(0, Materials.Num()), not Materials.Num()-1
                            {
                                materialIndex = 0;
                            }

                            var m       = materialIndex < originalMesh.Materials?.Length ? originalMesh.Materials[materialIndex].Material : null;
                            sections[j] = new CMeshSection(m, srcLod.Sections[j].BaseIndex, srcLod.Sections[j].NumTriangles);
                        }
                        return(sections);
                    })
                };

                var bUseVerticesFromSections = false;
                var vertexCount = srcLod.VertexBufferGPUSkin.GetVertexCount();
                if (vertexCount == 0 && srcLod.Sections.Length > 0 && srcLod.Sections[0].SoftVertices.Length > 0)
                {
                    bUseVerticesFromSections = true;
                    for (var j = 0; j < srcLod.Sections.Length; j++)
                    {
                        vertexCount += srcLod.Sections[i].SoftVertices.Length;
                    }
                }

                convertedMesh.LODs[i].AllocateVerts(vertexCount);

                var  chunkIndex       = -1;
                var  chunkVertexIndex = 0;
                long lastChunkVertex  = -1;
                ushort[]? boneMap = null;
                var vertBuffer = srcLod.VertexBufferGPUSkin;

                if (srcLod.ColorVertexBuffer.Data.Length == vertexCount)
                {
                    convertedMesh.LODs[i].AllocateVertexColorBuffer();
                }

                for (var vert = 0; vert < vertexCount; vert++)
                {
                    while (vert >= lastChunkVertex) // this will fix any issues with empty chunks or sections
                    {
                        // proceed to next chunk or section
                        if (srcLod.Chunks.Length > 0)
                        {
                            // pre-UE4.13 code: chunks
                            var c = srcLod.Chunks[++chunkIndex];
                            lastChunkVertex = c.BaseVertexIndex + c.NumRigidVertices + c.NumSoftVertices;
                            boneMap         = c.BoneMap;
                        }
                        else
                        {
                            // UE4.13+ code: chunk information migrated to sections
                            var s = srcLod.Sections[++chunkIndex];
                            lastChunkVertex = s.BaseVertexIndex + s.NumVertices;
                            boneMap         = s.BoneMap;
                        }
                        chunkVertexIndex = 0;
                    }

                    FSkelMeshVertexBase v; // has everything but UV[]
                    if (bUseVerticesFromSections)
                    {
                        var v0 = srcLod.Sections[chunkIndex].SoftVertices[chunkVertexIndex++];
                        v = v0;

                        // UV: simply copy float data
                        convertedMesh.LODs[i].Verts[vert].UV = v0.UV[0];
                        for (var texCoordIndex = 1; texCoordIndex < numTexCoords; texCoordIndex++)
                        {
                            convertedMesh.LODs[i].ExtraUV.Value[texCoordIndex - 1][vert] = v0.UV[texCoordIndex];
                        }
                    }
                    else if (!vertBuffer.bUseFullPrecisionUVs)
                    {
                        var v0 = vertBuffer.VertsHalf[vert];
                        v = v0;

                        // UV: convert half -> float
                        convertedMesh.LODs[i].Verts[vert].UV = (FMeshUVFloat)v0.UV[0];
                        for (var texCoordIndex = 1; texCoordIndex < numTexCoords; texCoordIndex++)
                        {
                            convertedMesh.LODs[i].ExtraUV.Value[texCoordIndex - 1][vert] = (FMeshUVFloat)v0.UV[texCoordIndex];
                        }
                    }
                    else
                    {
                        var v0 = vertBuffer.VertsFloat[vert];
                        v = v0;

                        // UV: simply copy float data
                        convertedMesh.LODs[i].Verts[vert].UV = v0.UV[0];
                        for (var texCoordIndex = 1; texCoordIndex < numTexCoords; texCoordIndex++)
                        {
                            convertedMesh.LODs[i].ExtraUV.Value[texCoordIndex - 1][vert] = v0.UV[texCoordIndex];
                        }
                    }

                    convertedMesh.LODs[i].Verts[vert].Position = v.Pos;
                    UnpackNormals(v.Normal, convertedMesh.LODs[i].Verts[vert]);
                    if (convertedMesh.LODs[i].VertexColors != null)
                    {
                        convertedMesh.LODs[i].VertexColors[vert] = srcLod.ColorVertexBuffer.Data[vert];
                    }

                    var  i2            = 0;
                    uint packedWeights = 0;
                    for (var j = 0; j < 4; j++)
                    {
                        uint boneWeight = v.Infs.BoneWeight[j];
                        if (boneWeight == 0)
                        {
                            continue;                  // skip this influence (but do not stop the loop!)
                        }
                        packedWeights |= boneWeight << (i2 * 8);
                        convertedMesh.LODs[i].Verts[vert].Bone[i2] = (short)boneMap[v.Infs.BoneIndex[j]];
                        i2++;
                    }
                    convertedMesh.LODs[i].Verts[vert].PackedWeights = packedWeights;
                    if (i2 < 4)
                    {
                        convertedMesh.LODs[i].Verts[vert].Bone[i2] = -1;         // mark end of list
                    }
                }
            }

            var numBones = originalMesh.ReferenceSkeleton.FinalRefBoneInfo.Length;

            convertedMesh.RefSkeleton = new CSkelMeshBone[numBones];
            for (var i = 0; i < convertedMesh.RefSkeleton.Length; i++)
            {
                convertedMesh.RefSkeleton[i] = new CSkelMeshBone
                {
                    Name        = originalMesh.ReferenceSkeleton.FinalRefBoneInfo[i].Name,
                    ParentIndex = originalMesh.ReferenceSkeleton.FinalRefBoneInfo[i].ParentIndex,
                    Position    = originalMesh.ReferenceSkeleton.FinalRefBonePose[i].Translation,
                    Orientation = originalMesh.ReferenceSkeleton.FinalRefBonePose[i].Rotation,
                };

                // fix skeleton; all bones but 0
                if (i >= 1)
                {
                    convertedMesh.RefSkeleton[i].Orientation.Conjugate();
                }
            }

            convertedMesh.FinalizeMesh();
            return(true);
        }