unsafe private void FillVerticesUV2(FLVER2.Mesh mesh, Span <Vector3> pickingVerts, IntPtr vertBuffer) { Span <FlverLayoutUV2> verts = new Span <FlverLayoutUV2>(vertBuffer.ToPointer(), mesh.VertexCount); for (int i = 0; i < mesh.VertexCount; i++) { var vert = mesh.Vertices[i]; verts[i] = new FlverLayoutUV2(); pickingVerts[i] = new Vector3(vert.Position.X, vert.Position.Y, vert.Position.Z); fixed(FlverLayoutUV2 *v = &verts[i]) { FillVertex(ref (*v).Position, ref vert); FillNormalSNorm8((*v).Normal, ref vert); FillUVShort((*v).Uv1, ref vert, 0); FillUVShort((*v).Uv2, ref vert, 1); if (vert.TangentCount > 0) { FillBinormalBitangentSNorm8((*v).Binormal, (*v).Bitangent, ref vert, 0); } else { FillBinormalBitangentSNorm8Zero((*v).Binormal, (*v).Bitangent); } } } }
private List <FLVER.Bone> GetAllBonesReferencedByVertex(FLVER2 f, FLVER2.Mesh m, FLVER.Vertex v) { if (!PrecalculatedBoneLists.ContainsKey(v)) { List <FLVER.Bone> result = new List <FLVER.Bone>(); for (var i = 0; i < v.BoneIndices.Length; i++) { var vertBoneIndex = v.BoneIndices[i]; if (vertBoneIndex >= 0) { if (Importer.JOBCONFIG.UseDirectBoneIndices) { result.Add(f.Bones[vertBoneIndex]); } else { if (m.BoneIndices[vertBoneIndex] >= 0) { result.Add(f.Bones[m.BoneIndices[vertBoneIndex]]); } } } } PrecalculatedBoneLists.Add(v, result); } return(PrecalculatedBoneLists[v]); }
private static void GenerateLodAndMotionBlurFacesets(FLVER2.Mesh mesh) { var newFacesetsToAdd = new List <SoulsFormats.FLVER2.FaceSet>(); foreach (var faceset in mesh.FaceSets) { var lod1 = new SoulsFormats.FLVER2.FaceSet() { CullBackfaces = faceset.CullBackfaces, Flags = FLVER2.FaceSet.FSFlags.LodLevel1, TriangleStrip = faceset.TriangleStrip, Indices = faceset.Indices }; var lod2 = new FLVER2.FaceSet() { CullBackfaces = faceset.CullBackfaces, Flags = FLVER2.FaceSet.FSFlags.LodLevel2, TriangleStrip = faceset.TriangleStrip, Indices = faceset.Indices }; var mblur = new FLVER2.FaceSet() { CullBackfaces = faceset.CullBackfaces, Flags = FLVER2.FaceSet.FSFlags.MotionBlur, TriangleStrip = faceset.TriangleStrip, Indices = faceset.Indices }; var mblurlod1 = new FLVER2.FaceSet() { CullBackfaces = faceset.CullBackfaces, Flags = FLVER2.FaceSet.FSFlags.LodLevel1 | FLVER2.FaceSet.FSFlags.MotionBlur, TriangleStrip = faceset.TriangleStrip, Indices = faceset.Indices }; var mblurlod2 = new FLVER2.FaceSet() { CullBackfaces = faceset.CullBackfaces, Flags = FLVER2.FaceSet.FSFlags.LodLevel2 | FLVER2.FaceSet.FSFlags.MotionBlur, TriangleStrip = faceset.TriangleStrip, Indices = faceset.Indices }; newFacesetsToAdd.Add(lod1); newFacesetsToAdd.Add(lod2); newFacesetsToAdd.Add(mblur); newFacesetsToAdd.Add(mblurlod1); newFacesetsToAdd.Add(mblurlod2); } foreach (var lod in newFacesetsToAdd) { mesh.FaceSets.Add(lod); } }
public static void UpdateBoundingBox(this FLVER2.Mesh mesh, NVector3 vertexPos) { var minX = Math.Min(mesh.BoundingBox.Min.X, vertexPos.X); var minY = Math.Min(mesh.BoundingBox.Min.Y, vertexPos.Y); var minZ = Math.Min(mesh.BoundingBox.Min.Z, vertexPos.Z); var maxX = Math.Max(mesh.BoundingBox.Max.X, vertexPos.X); var maxY = Math.Max(mesh.BoundingBox.Max.Y, vertexPos.Y); var maxZ = Math.Max(mesh.BoundingBox.Max.Z, vertexPos.Z); mesh.BoundingBox.Min = new NVector3(minX, minY, minZ); mesh.BoundingBox.Max = new NVector3(maxX, maxY, maxZ); }
unsafe private void FillVerticesNormalOnly(FLVER2.Mesh mesh, Span <Vector3> pickingVerts, IntPtr vertBuffer) { Span <FlverLayoutSky> verts = new Span <FlverLayoutSky>(vertBuffer.ToPointer(), mesh.VertexCount); for (int i = 0; i < mesh.VertexCount; i++) { var vert = mesh.Vertices[i]; verts[i] = new FlverLayoutSky(); pickingVerts[i] = new Vector3(vert.Position.X, vert.Position.Y, vert.Position.Z); fixed(FlverLayoutSky *v = &verts[i]) { FillVertex(ref (*v).Position, ref vert); FillNormalSNorm8((*v).Normal, ref vert); } } }
public ImportedFLVER2Model ImportFromAssimpScene(Scene scene, FLVER2ImportSettings settings) { LoadMaterialInfoBankForGame(settings.Game); var result = new ImportedFLVER2Model(); var flver = result.Flver = new FLVER2(); flver.Header.BigEndian = settings.FlverHeader.BigEndian; flver.Header.BoundingBoxMax = new NVector3(float.MinValue); flver.Header.BoundingBoxMin = new NVector3(float.MaxValue); flver.Header.Unicode = settings.FlverHeader.Unicode; flver.Header.Unk4A = settings.FlverHeader.Unk4A; flver.Header.Unk4C = settings.FlverHeader.Unk4C; flver.Header.Unk5C = settings.FlverHeader.Unk5C; flver.Header.Unk5D = settings.FlverHeader.Unk5D; flver.Header.Unk68 = settings.FlverHeader.Unk68; flver.Header.Version = settings.FlverHeader.Version; var flverSceneMatrix = NMatrix.CreateScale(NVector3.One * settings.SceneScale); if (settings.ConvertFromZUp) { flverSceneMatrix *= SapMath.ZUpToYUpNMatrix; } //flverSceneMatrix *= NMatrix.CreateRotationY(SapMath.Pi); flverSceneMatrix *= settings.SceneCorrectMatrix; flverSceneMatrix *= NMatrix.CreateScale(1, 1, -1); var coordMat = AssimpUtilities.GetSceneCoordSystemMatrix(scene); scene.RootNode.Transform *= coordMat; var skeletonRootNode = AssimpUtilities.FindRootNode(scene, settings.RootNodeName, out Matrix4x4 skeletonRootNodeMatrix); var metaskeleton = FLVERImportHelpers.GenerateFlverMetaskeletonFromRootNode( skeletonRootNode, skeletonRootNodeMatrix, settings.SceneScale); flver.Bones = metaskeleton.Bones; flver.Dummies = metaskeleton.DummyPoly; foreach (var b in flver.Bones) { // Mark as dummied-out bone until iterating over them later and seeing which are weighted to meshes. if (b.ParentIndex == -1) { b.Unk3C = 1; } } var usesIndirectBones = flver.Header.Version <= 0x20010; if (settings.SkeletonTransformsOverride != null) { flver.Bones = settings.SkeletonTransformsOverride; } //var flverMaterialList = new List<FLVER2.Material>(); foreach (var material in scene.Materials) { string[] materialNameSplit = material.Name.Split('|'); string mtd = materialNameSplit.Length > 1 ? materialNameSplit[1].Trim() + ".mtd" : null; // If MTD doesn't exist, use original mtd = MaterialInfoBankPerGame[settings.Game].FallbackToDefaultMtdIfNecessary(mtd, Logger); //ErrorTODO: materialNameSplit should be 2 items long. var flverMaterial = new FLVER2.Material(materialNameSplit[0].Trim(), mtd, 0); void AddTextureSlot(TextureSlot slot, string ingameSlot) { flverMaterial.Textures.Add(new FLVER2.Texture(type: ingameSlot, path: slot.FilePath != null ? Path.GetFullPath(slot.FilePath) : "", scale: System.Numerics.Vector2.One, 1, true, 0, 0, 0)); string texName = Path.GetFileNameWithoutExtension(slot.FilePath); byte[] texData = scene.GetEmbeddedTexture(slot.FilePath)?.CompressedData; if (texData != null) { var ddsFormat = TPFTextureFormatFinder.GetTpfFormatFromDdsBytes(texData); result.Textures.Add(new TPF.Texture(texName, format: ddsFormat, flags1: 0, bytes: texData)); } } var materialDefinition = MaterialInfoBankPerGame[settings.Game].MaterialDefs[mtd.ToLower()]; var texChanDefs = materialDefinition.TextureChannels; foreach (var kvp in texChanDefs) { if (kvp.Key.Index == 0) { if (kvp.Key.Semantic == TextureChannelSemantic.Diffuse) { AddTextureSlot(material.TextureDiffuse, kvp.Value); } else if (kvp.Key.Semantic == TextureChannelSemantic.Specular) { AddTextureSlot(material.TextureSpecular, kvp.Value); } else if (kvp.Key.Semantic == TextureChannelSemantic.Normals) { AddTextureSlot(material.TextureNormal, kvp.Value); } else if (kvp.Key.Semantic == TextureChannelSemantic.Emissive) { AddTextureSlot(material.TextureEmissive, kvp.Value); } else { flverMaterial.Textures.Add(new FLVER2.Texture(type: kvp.Value, path: string.Empty, scale: System.Numerics.Vector2.One, 0, false, 0, 0, 0)); } } } if (materialDefinition.GXItems.Count > 0) { flverMaterial.GXIndex = flver.GXLists.Count; var gxList = new FLVER2.GXList(); for (int i = 0; i < materialDefinition.GXItems.Count; i++) { var gxid = materialDefinition.GXItems[i].GXID; var unk04 = materialDefinition.GXItems[i].Unk04; byte[] data = MaterialInfoBankPerGame[settings.Game].DefaultGXItemDataExamples[mtd][i]; gxList.Add(new FLVER2.GXItem(gxid, unk04, data)); } flver.GXLists.Add(gxList); } flver.Materials.Add(flverMaterial); //flverMaterialList.Add(flverMaterial); } //var properBoneParentRegistry = new Dictionary<Bone, string>(); //foreach (var mesh in scene.Meshes) //{ // foreach (var b in mesh.Bones) // { // bool alreadyRegistered = false; // foreach (var bone in properBoneParentRegistry.Keys) // { // if (bone.Name == b.Name) // { // alreadyRegistered = true; // break; // } // } // if (alreadyRegistered) // continue; // mesh. // properBoneParentRegistry.Add(b, b.) // } //} if (settings.BoneNameRemapper != null) { foreach (var bn in settings.BoneNameRemapper) { var bone = flver.Bones.FindIndex(b => b.Name == bn.Key); if (bone >= 0) { flver.Bones[bone].Name = bn.Value; } } } foreach (var mesh in scene.Meshes) { var flverMesh = new FLVER2.Mesh(); flverMesh.BoundingBox = new FLVER2.Mesh.BoundingBoxes(); //TODO: ACTUALLY READ FROM THINGS flverMesh.Dynamic = 1; // Register mesh transform bone: //flverMesh.DefaultBoneIndex = flver.Bones.Count; //int flverLastRootBoneIndex = flver.Bones.FindLastIndex(b => b.ParentIndex == -1); //// Register this new bone as a sibling. //if (flverLastRootBoneIndex >= 0) // flver.Bones[flverLastRootBoneIndex].NextSiblingIndex = (short)flverMesh.DefaultBoneIndex; //flver.Bones.Add(new FLVER.Bone() //{ // Name = mesh.Name, // Translation = NVector3.Zero, // Rotation = NVector3.Zero, // Scale = NVector3.One, // BoundingBoxMin = NVector3.One * -0.05f, // BoundingBoxMax = NVector3.One * 0.05f, // // Cross-register sibling from above. // PreviousSiblingIndex = (short)flverLastRootBoneIndex, // NextSiblingIndex = -1, // ParentIndex = -1, // ChildIndex = -1, // Unk3C = 1, //}); int meshUVCount = 0; for (int i = 0; i < mesh.UVComponentCount.Length; i++) { if (mesh.UVComponentCount[i] > 0) { meshUVCount++; } } if (mesh.PrimitiveType != PrimitiveType.Triangle) { Console.WriteLine(); } var flverFaceSet = new FLVER2.FaceSet(); //flverFaceSet.TriangleStrip = true; // Handle vertex buffers / layouts: flverMesh.MaterialIndex = mesh.MaterialIndex; //var newMat = flverMaterialList[mesh.MaterialIndex]; //var indexOfNewMat = flver.Materials.IndexOf(newMat); //if (indexOfNewMat >= 0) //{ // flverMesh.MaterialIndex = indexOfNewMat; //} //else //{ // flverMesh.MaterialIndex = flver.Materials.Count; // flver.Materials.Add(newMat); //} var flverMaterial = flver.Materials[flverMesh.MaterialIndex]; var matDefinition = MaterialInfoBankPerGame[settings.Game].MaterialDefs[flverMaterial.MTD.ToLower()]; var defaultBufferDeclaration = matDefinition.AcceptableVertexBufferDeclarations[0]; Dictionary <FLVER.LayoutSemantic, int> requiredVertexBufferMembers = new Dictionary <FLVER.LayoutSemantic, int>(); foreach (var buff in defaultBufferDeclaration.Buffers) { foreach (var m in buff) { if (!requiredVertexBufferMembers.ContainsKey(m.Semantic)) { requiredVertexBufferMembers.Add(m.Semantic, 0); } requiredVertexBufferMembers[m.Semantic]++; } int nextLayoutIndex = flver.BufferLayouts.Count; flver.BufferLayouts.Add(buff); var vertBuffer = new FLVER2.VertexBuffer(nextLayoutIndex); flverMesh.VertexBuffers.Add(vertBuffer); } flverMesh.Vertices = new List <FLVER.Vertex>(mesh.VertexCount); for (int i = 0; i < mesh.VertexCount; i++) { var newVert = new FLVER.Vertex(uvCapacity: meshUVCount, //TODO: Figure out what multiple tangents are used for etc and implement all // of that into the XML vert layout system stuff etc etc. tangentCapacity: mesh.HasTangentBasis ? 1 : 0, colorCapacity: mesh.VertexColorChannelCount); newVert.Position = NVector3.Transform(mesh.Vertices[i].ToNumerics(), flverSceneMatrix); flver.Header.UpdateBoundingBox(newVert.Position); if (flverMesh.BoundingBox != null) { flverMesh.UpdateBoundingBox(newVert.Position); } newVert.Normal = NVector3.Normalize(NVector3.TransformNormal(mesh.Normals[i].ToNumerics(), flverSceneMatrix)); //TODO: TEST THIS AGAINST OTHER GAMES ETC //newVert.NormalW = 127; if (mesh.HasTangentBasis) { //ErrorTODO: Throw error if mesh somehow has tangents but not normals. var tan = mesh.Tangents[i]; var bitanXYZ = mesh.BiTangents[i]; //TODO: Check Bitangent W calculation var bitanW = Vector3D.Dot(Vector3D.Cross(tan, mesh.Normals[i]), bitanXYZ) >= 0 ? 1 : -1; var bitanXYZTransformed = NVector3.Normalize(NVector3.TransformNormal(bitanXYZ.ToNumerics(), flverSceneMatrix)); newVert.Tangents.Add(new System.Numerics.Vector4(bitanXYZTransformed, bitanW)); //TODO: CHECK THIS AND SEE WTF IT EVEN IS SUPPOSED TO BE newVert.Bitangent = new System.Numerics.Vector4( NVector3.TransformNormal(tan.ToNumerics(), flverSceneMatrix), 0); } for (int j = 0; j < meshUVCount; j++) { var uv = mesh.TextureCoordinateChannels[j][i]; newVert.UVs.Add(new NVector3(uv.X, 1 - uv.Y, uv.Z)); } for (int j = 0; j < mesh.VertexColorChannelCount; j++) { newVert.Colors.Add(mesh.VertexColorChannels[j][i].ToFlverVertexColor()); } for (int j = 0; j < 4; j++) { newVert.BoneIndices[j] = -1; } newVert.EnsureLayoutMembers(requiredVertexBufferMembers); flverMesh.Vertices.Add(newVert); } if (usesIndirectBones) { var bonesInMesh = mesh.Bones.OrderByDescending(mb => mb.VertexWeightCount).ToList(); foreach (var bone in bonesInMesh) { var boneIndex = flver.Bones.FindIndex(b => b.Name == bone.Name); if (!flverMesh.BoneIndices.Contains(boneIndex)) { flverMesh.BoneIndices.Add(boneIndex); } } flverMesh.BoneIndices = flverMesh.BoneIndices.OrderBy(idx => idx).ToList(); } foreach (var bone in mesh.Bones) { var boneIndex = flver.Bones.FindIndex(b => b.Name == bone.Name); if (boneIndex == -1) { Logger.LogWarning($"No bone with exact name '{bone.Name}' found. Looking for a bone that starts with that name"); boneIndex = flver.Bones.FindIndex(b => b.Name.StartsWith(bone.Name)); } var boneDoesNotExist = false; // Mark bone as not-dummied-out since there is geometry skinned to it. if (boneIndex >= 0 && boneIndex < flver.Bones.Count) { flver.Bones[boneIndex].Unk3C = 0; } else { Logger.LogWarning($"Vertex skinned to bone '{bone.Name}' which does NOT exist in the skeleton."); boneDoesNotExist = true; } int GetNextAvailableBoneSlotOfVert(int vertIndex) { if (flverMesh.Vertices[vertIndex].BoneIndices[0] < 0) { return(0); } else if (flverMesh.Vertices[vertIndex].BoneIndices[1] < 0) { return(1); } else if (flverMesh.Vertices[vertIndex].BoneIndices[2] < 0) { return(2); } else if (flverMesh.Vertices[vertIndex].BoneIndices[3] < 0) { return(3); } else { return(-1); } } foreach (var weight in bone.VertexWeights) { int boneSlot = GetNextAvailableBoneSlotOfVert(weight.VertexID); if (boneSlot >= 0) { var indexToAssign = usesIndirectBones ? flverMesh.BoneIndices.IndexOf(boneIndex) : boneIndex; if (indexToAssign == -1) { Console.WriteLine("fatcat"); } flverMesh.Vertices[weight.VertexID].BoneIndices[boneSlot] = boneDoesNotExist ? 0 : indexToAssign; flverMesh.Vertices[weight.VertexID].BoneWeights[boneSlot] = boneDoesNotExist ? 0 : weight.Weight; if (!boneDoesNotExist) { flver.Bones[boneIndex].UpdateBoundingBox(flver.Bones, flverMesh.Vertices[weight.VertexID].Position); } } } } for (int i = 0; i < flverMesh.Vertices.Count; i++) { float weightMult = 1 / ( flverMesh.Vertices[i].BoneWeights[0] + flverMesh.Vertices[i].BoneWeights[1] + flverMesh.Vertices[i].BoneWeights[2] + flverMesh.Vertices[i].BoneWeights[3]); for (int j = 0; j < 4; j++) { //flverMesh.Vertices[i].BoneWeights[j] = flverMesh.Vertices[i].BoneWeights[j] * weightMult; if (flverMesh.Vertices[i].BoneIndices[j] < 0) { flverMesh.Vertices[i].BoneIndices[j] = 0; } } //TODO: TEST THIS AGAINST OTHER GAMES ETC if (!requiredVertexBufferMembers.ContainsKey(FLVER.LayoutSemantic.BoneIndices)) { flverMesh.Vertices[i].NormalW = flverMesh.Vertices[i].BoneIndices[0]; } } //foreach (var face in mesh.Faces) //{ // //TODO: See if resets need to be added inbetween or anything. // flverFaceSet.Indices.AddRange(face.Indices); //} flverFaceSet.Indices.AddRange(mesh.GetIndices()); flverMesh.FaceSets.Add(flverFaceSet); GenerateLodAndMotionBlurFacesets(flverMesh); flver.Meshes.Add(flverMesh); } // DEBUGGING //flver.Bones.RemoveAt(0); //foreach (var mm in flver.Meshes) // for (int mbi = 0; mbi < mm.BoneIndices.Count; mbi++) // mm.BoneIndices[mbi] = mm.BoneIndices[mbi] - 1; //foreach (var b in flver.Bones) //{ // if (b.ParentIndex >= 0) // b.ParentIndex--; // if (b.ChildIndex >= 0) // b.ChildIndex--; // if (b.NextSiblingIndex >= 0) // b.NextSiblingIndex--; // if (b.PreviousSiblingIndex >= 0) // b.PreviousSiblingIndex--; //} /////////////////// foreach (var b in flver.Bones) { if (settings.SkeletonTransformsOverride != null) { var match = settings.SkeletonTransformsOverride.FindIndex(bn => bn.Name == b.Name); if (match >= 0) { b.Translation = settings.SkeletonTransformsOverride[match].Translation; b.Rotation = settings.SkeletonTransformsOverride[match].Rotation; b.Scale = settings.SkeletonTransformsOverride[match].Scale; } } if (float.IsInfinity(b.BoundingBoxMin.X) || float.IsInfinity(b.BoundingBoxMin.Y) || float.IsInfinity(b.BoundingBoxMin.Z) || float.IsInfinity(b.BoundingBoxMax.X) || float.IsInfinity(b.BoundingBoxMax.Y) || float.IsInfinity(b.BoundingBoxMax.Z)) { b.BoundingBoxMin = NVector3.One * -0.1f; b.BoundingBoxMax = NVector3.One * 0.1f; } } return(result); }
unsafe private void ProcessMesh(FLVER2.Mesh mesh, FlverSubmesh dest) { var factory = Scene.Renderer.Factory; dest.Material = GPUMaterials[mesh.MaterialIndex]; //var MeshVertices = VerticesPool.Rent(mesh.VertexCount); var vSize = dest.Material.VertexSize; var meshVertices = Marshal.AllocHGlobal(mesh.VertexCount * (int)vSize); dest.PickingVertices = Marshal.AllocHGlobal(mesh.VertexCount * sizeof(Vector3)); var pvhandle = new Span <Vector3>(dest.PickingVertices.ToPointer(), mesh.VertexCount); if (dest.Material.LayoutType == MeshLayoutType.LayoutSky) { FillVerticesNormalOnly(mesh, pvhandle, meshVertices); } else if (dest.Material.LayoutType == MeshLayoutType.LayoutUV2) { FillVerticesUV2(mesh, pvhandle, meshVertices); } else { FillVerticesStandard(mesh, pvhandle, meshVertices); } dest.VertexCount = mesh.VertexCount; dest.MeshFacesets = new List <FlverSubmesh.FlverSubmeshFaceSet>(); var facesets = mesh.FaceSets; var fsUploadsPending = facesets.Count(); bool is32bit = Flver.Header.Version > 0x20005 && mesh.VertexCount > 65535; int indicesTotal = 0; ushort[] fs16 = null; int[] fs32 = null; foreach (var faceset in facesets) { indicesTotal += faceset.Indices.Length; } if (is32bit) { fs32 = new int[indicesTotal]; } else { fs16 = new ushort[indicesTotal]; } int idxoffset = 0; foreach (var faceset in facesets) { if (faceset.Indices.Length == 0) { continue; } //At this point they use 32-bit faceset vertex indices uint buffersize = (uint)faceset.IndicesCount * (is32bit ? 4u : 2u); var indices = faceset.TriangleStrip ? faceset.Triangulate(true).ToArray() : faceset.Indices.ToArray(); var newFaceSet = new FlverSubmesh.FlverSubmeshFaceSet() { BackfaceCulling = faceset.CullBackfaces, IsTriangleStrip = faceset.TriangleStrip, //IndexBuffer = factory.CreateBuffer(new BufferDescription(buffersize, BufferUsage.IndexBuffer)), IndexOffset = idxoffset, IndexCount = faceset.IndicesCount, Is32Bit = is32bit, PickingIndicesCount = indices.Length, //PickingIndices = Marshal.AllocHGlobal(indices.Length * 4), }; fixed(void *iptr = indices) { //Unsafe.CopyBlock(newFaceSet.PickingIndices.ToPointer(), iptr, (uint)indices.Length * 4); } if ((faceset.Flags & FLVER2.FaceSet.FSFlags.LodLevel1) > 0) { newFaceSet.LOD = 1; //HasNoLODs = false; newFaceSet.IsMotionBlur = false; } else if ((faceset.Flags & FLVER2.FaceSet.FSFlags.LodLevel2) > 0) { newFaceSet.LOD = 2; //HasNoLODs = false; newFaceSet.IsMotionBlur = false; } if ((faceset.Flags & FLVER2.FaceSet.FSFlags.MotionBlur) > 0) { newFaceSet.IsMotionBlur = true; } if (is32bit) { for (int i = 0; i < faceset.Indices.Length; i++) { if (faceset.Indices[i] == 0xFFFF && faceset.Indices[i] > mesh.Vertices.Length) { fs32[newFaceSet.IndexOffset + i] = -1; } else { fs32[newFaceSet.IndexOffset + i] = faceset.Indices[i]; } } } else { for (int i = 0; i < faceset.Indices.Length; i++) { if (faceset.Indices[i] == 0xFFFF && faceset.Indices[i] > mesh.Vertices.Length) { fs16[newFaceSet.IndexOffset + i] = 0xFFFF; } else { fs16[newFaceSet.IndexOffset + i] = (ushort)faceset.Indices[i]; } } } dest.MeshFacesets.Add(newFaceSet); idxoffset += faceset.Indices.Length; } dest.Bounds = BoundingBox.CreateFromPoints((Vector3 *)dest.PickingVertices.ToPointer(), dest.VertexCount, 12, Quaternion.Identity, Vector3.Zero, Vector3.One); uint vbuffersize = (uint)mesh.VertexCount * (uint)vSize; dest.GeomBuffer = Scene.Renderer.GeometryBufferAllocator.Allocate(vbuffersize, (uint)indicesTotal * (is32bit ? 4u : 2u), (int)vSize, 4, (h) => { h.FillVBuffer(meshVertices, vSize * (uint)mesh.VertexCount, () => { Marshal.FreeHGlobal(meshVertices); }); if (is32bit) { h.FillIBuffer(fs32); } else { h.FillIBuffer(fs16); } }); facesets = null; if (CaptureMaterialLayouts) { lock (_matLayoutLock) { if (!MaterialLayouts.ContainsKey(dest.Material.MaterialName)) { MaterialLayouts.Add(dest.Material.MaterialName, Flver.BufferLayouts[mesh.VertexBuffers[0].LayoutIndex]); } } } if (mesh.DefaultBoneIndex != -1 && mesh.DefaultBoneIndex < Bones.Count) { dest.LocalTransform = Utils.GetBoneObjectMatrix(Bones[mesh.DefaultBoneIndex], Bones); } Marshal.FreeHGlobal(dest.PickingVertices); }
public FlverSubmeshRenderer(Model parent, FLVER2 flvr, FLVER2.Mesh mesh) { Parent = parent; MaterialName = flvr.Materials[mesh.MaterialIndex].Name; var shortMaterialName = MiscUtil.GetFileNameWithoutDirectoryOrExtension(flvr.Materials[mesh.MaterialIndex].MTD); if (shortMaterialName.EndsWith("_Alp") || shortMaterialName.Contains("_Edge") || shortMaterialName.Contains("_Decal") || shortMaterialName.Contains("_Cloth") || shortMaterialName.Contains("_al") || shortMaterialName.Contains("BlendOpacity")) { DrawStep = GFXDrawStep.AlphaEdge; } else { DrawStep = GFXDrawStep.Opaque; } bool hasLightmap = false; foreach (var matParam in flvr.Materials[mesh.MaterialIndex].Textures) { var paramNameCheck = matParam.Type.ToUpper(); // DS3/BB if (paramNameCheck == "G_DIFFUSETEXTURE") { TexNameDiffuse = matParam.Path; } else if (paramNameCheck == "G_SPECULARTEXTURE") { TexNameSpecular = matParam.Path; } else if (paramNameCheck == "G_BUMPMAPTEXTURE") { TexNameNormal = matParam.Path; } else if (paramNameCheck == "G_DOLTEXTURE1") { TexNameDOL1 = matParam.Path; hasLightmap = true; } else if (paramNameCheck == "G_DOLTEXTURE2") { TexNameDOL2 = matParam.Path; } // DS1 params else if (paramNameCheck == "G_DIFFUSE") { TexNameDiffuse = matParam.Path; } else if (paramNameCheck == "G_SPECULAR") { TexNameSpecular = matParam.Path; } else if (paramNameCheck == "G_BUMPMAP") { TexNameNormal = matParam.Path; } else if (paramNameCheck == "G_LIGHTMAP") { TexNameDOL1 = matParam.Path; hasLightmap = true; } // Alternate material params that work as diffuse } // MTD lookup MTD mtd = null; //InterrootLoader.GetMTD(flvr.Materials[mesh.MaterialIndex].MTD); var MeshVertices = new VertexPositionColorNormalTangentTexture[mesh.Vertices.Count]; for (int i = 0; i < mesh.Vertices.Count; i++) { var vert = mesh.Vertices[i]; MeshVertices[i] = new VertexPositionColorNormalTangentTexture(); MeshVertices[i].Position = new Vector3(vert.Position.X, vert.Position.Y, vert.Position.Z); if (vert.Normal != null && vert.Tangents != null && vert.Tangents.Count > 0) { MeshVertices[i].Normal = Vector3.Normalize(new Vector3(vert.Normal.X, vert.Normal.Y, vert.Normal.Z)); MeshVertices[i].Tangent = Vector3.Normalize(new Vector3(vert.Tangents[0].X, vert.Tangents[0].Y, vert.Tangents[0].Z)); MeshVertices[i].Binormal = Vector3.Cross(Vector3.Normalize(MeshVertices[i].Normal), Vector3.Normalize(MeshVertices[i].Tangent)) * vert.Tangents[0].W; } if (vert.UVs.Count > 0) { MeshVertices[i].TextureCoordinate = new Vector2(vert.UVs[0].X, vert.UVs[0].Y); if (vert.UVs.Count > 1 && hasLightmap) { if (mtd == null) { // Really stupid heuristic to determine light map UVs without reading mtd files or something if (vert.UVs.Count > 2 && flvr.Materials[mesh.MaterialIndex].Textures.Count > 11) { MeshVertices[i].TextureCoordinate2 = new Vector2(vert.UVs[2].X, vert.UVs[2].Y); } else { MeshVertices[i].TextureCoordinate2 = new Vector2(vert.UVs[1].X, vert.UVs[1].Y); } } else { // Better heuristic with MTDs int uvindex = mtd.Textures.Find(tex => tex.Type.ToUpper() == "G_LIGHTMAP" || tex.Type.ToUpper() == "G_DOLTEXTURE1").UVNumber; int uvoffset = 1; for (int j = 1; j < uvindex; j++) { if (!mtd.Textures.Any(t => (t.UVNumber == j))) { uvoffset++; } } uvindex -= uvoffset; if (vert.UVs.Count > uvindex) { MeshVertices[i].TextureCoordinate2 = new Vector2(vert.UVs[uvindex].X, vert.UVs[uvindex].Y); } else { MeshVertices[i].TextureCoordinate2 = new Vector2(vert.UVs[1].X, vert.UVs[1].Y); } } } else { MeshVertices[i].TextureCoordinate2 = Vector2.Zero; } } else { MeshVertices[i].TextureCoordinate = Vector2.Zero; MeshVertices[i].TextureCoordinate2 = Vector2.Zero; } } VertexCount = MeshVertices.Length; MeshFacesets = new List <FlverSubmeshRendererFaceSet>(); foreach (var faceset in mesh.FaceSets) { //At this point they use 32-bit faceset vertex indices bool is32bit = flvr.Header.Version > 0x20005; var newFaceSet = new FlverSubmeshRendererFaceSet() { BackfaceCulling = faceset.CullBackfaces, IsTriangleStrip = faceset.TriangleStrip, IndexBuffer = new IndexBuffer( GFX.Device, is32bit ? IndexElementSize.ThirtyTwoBits : IndexElementSize.SixteenBits, faceset.Indices.Count, BufferUsage.WriteOnly), IndexCount = faceset.Indices.Count, }; if (faceset.Flags == FLVER2.FaceSet.FSFlags.LodLevel1) { newFaceSet.LOD = 1; HasNoLODs = false; } else if (faceset.Flags == FLVER2.FaceSet.FSFlags.LodLevel2) { newFaceSet.LOD = 2; HasNoLODs = false; } if (is32bit) { newFaceSet.IndexBuffer.SetData(faceset.Indices.Select(x => (x == 0xFFFF && x > mesh.Vertices.Count) ? -1 : x).ToArray()); } else { newFaceSet.IndexBuffer.SetData(faceset.Indices.Select(x => (x == 0xFFFF && x > mesh.Vertices.Count) ? -1 : (ushort)x).ToArray()); } MeshFacesets.Add(newFaceSet); } Bounds = BoundingBox.CreateFromPoints(MeshVertices.Select(x => x.Position)); VertBuffer = new VertexBuffer(GFX.Device, typeof(VertexPositionColorNormalTangentTexture), MeshVertices.Length, BufferUsage.WriteOnly); VertBuffer.SetData(MeshVertices); VertBufferBinding = new VertexBufferBinding(VertBuffer, 0, 0); TryToLoadTextures(); }
public MeshExporter(FbxScene scene, FLVER2.Mesh mesh) : base(scene, new MeshExportData { mesh = mesh }) { }