private unsafe void FillUVFloat(ref Vector2 dest, ref FLVER.Vertex v, byte index) { var uv = v.GetUV(index); dest.X = uv.X; dest.Y = uv.Y; }
public static void EnsureLayoutMembers(this FLVER.Vertex v, Dictionary <FLVER.LayoutSemantic, int> members) { foreach (var m in members) { if (m.Key == FLVER.LayoutSemantic.Tangent) { while (v.Tangents.Count < m.Value) { v.Tangents.Add(v.Tangents.Count > 0 ? v.Tangents[0] : System.Numerics.Vector4.Zero); } } else if (m.Key == FLVER.LayoutSemantic.VertexColor) { while (v.Colors.Count < m.Value) { v.Colors.Add(v.Colors.Count > 0 ? v.Colors[0] : new FLVER.VertexColor(0, 0, 0, 0)); } } else if (m.Key == FLVER.LayoutSemantic.UV) { while (v.UVs.Count <= m.Value) { v.UVs.Add(v.UVs.Count > 0 ? v.UVs[0] : NVector3.Zero); } } } }
private unsafe void FillUVShort(short *dest, ref FLVER.Vertex v, byte index) { var uv = v.GetUV(index); dest[0] = (short)(uv.X * 2048.0f); dest[1] = (short)(uv.Y * 2048.0f); }
private unsafe void FillNormalSNorm8(sbyte *dest, ref FLVER.Vertex v) { var n = Vector3.Normalize(new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z)); dest[0] = (sbyte)(n.X * 127.0f); dest[1] = (sbyte)(n.Y * 127.0f); dest[2] = (sbyte)(n.Z * 127.0f); }
static FLVER.Vertex generateVertex2tan(Vector3 pos, Vector3 uv1, Vector3 uv2, Vector3 normal, Vector3 tangets, Vector3 tangets2, int tangentW = -1) { FLVER.Vertex ans = new FLVER.Vertex(); ans.Positions = new List <Vector3>(); ans.Positions.Add(pos); ans.BoneIndices = new int[4] { 0, 0, 0, 0 }; ans.BoneWeights = new float[4] { 1, 0, 0, 0 }; ans.UVs = new List <Vector3>(); ans.UVs.Add(uv1); ans.UVs.Add(uv2); ans.Normals = new List <Vector4>(); ans.Normals.Add(new Vector4(normal.X, normal.Y, normal.Z, -1f)); ans.Tangents = new List <Vector4>(); ans.Tangents.Add(new Vector4(tangets.X, tangets.Y, tangets.Z, tangentW)); ans.Tangents.Add(new Vector4(tangets2.X, tangets2.Y, tangets2.Z, tangentW)); ans.Colors = new List <FLVER.Vertex.Color>(); ans.Colors.Add(new FLVER.Vertex.Color(255, 255, 255, 255)); return(ans); }
static void ExportModel(MenuCommand menuCommand) { if (Selection.activeObject == null) { return; } var assetPath = AssetDatabase.GetAssetPath(Selection.activeObject); if (!assetPath.ToUpper().EndsWith(".FBX")) { EditorUtility.DisplayDialog("Invalid asset", "Please select an fbx asset", "Ok"); } // Load the FBX as a prefab //GameObject obj = PrefabUtility.LoadPrefabContents(assetPath); GameObject obj = AssetDatabase.LoadAssetAtPath <GameObject>(assetPath); // Go through everything and strip the prefixes fbx exporters love to add Stack <GameObject> gameObjects = new Stack <GameObject>(); GameObject bonesRoot = null; GameObject meshesRoot = null; gameObjects.Push(obj); while (gameObjects.Count > 0) { var o = gameObjects.Pop(); o.name = o.name.Split(':').Last(); if (o.name == "Bones") { bonesRoot = o; } if (o.name == "Meshes") { meshesRoot = o; } for (int i = 0; i < o.transform.childCount; i++) { gameObjects.Push(o.transform.GetChild(i).gameObject); } } // Load the source c0000 and target flvers /*var sourceBnd = BND4.Read($@"{DarkSoulsTools.Interroot}\chr\c0000.chrbnd.dcx"); * var sourceFlver = FLVER.Read(sourceBnd.Files.Where(x => x.Name.ToUpper().EndsWith(".FLVER")).First().Bytes); * var targetBnd = BND4.Read($@"{DarkSoulsTools.Interroot}\parts\lg_m_9000.partsbnd.dcx"); * var targetFlver = FLVER.Read(targetBnd.Files.Where(x => x.Name.ToUpper().EndsWith(".FLVER")).First().Bytes);*/ var sourceBnd = BND4.Read($@"{DarkSoulsTools.Interroot}\chr\c4033.chrbnd.dcx"); var sourceFlver = FLVER.Read(sourceBnd.Files.Where(x => x.Name.ToUpper().EndsWith(".FLVER")).First().Bytes); var targetBnd = BND4.Read($@"{DarkSoulsTools.Interroot}\chr\c4033.chrbnd.dcx"); var targetFlver = FLVER.Read(targetBnd.Files.Where(x => x.Name.ToUpper().EndsWith(".FLVER")).First().Bytes); // Build a bone reindexing table Dictionary <string, int> SourceBoneTable = new Dictionary <string, int>(); for (int i = 0; i < sourceFlver.Bones.Count; i++) { if (!SourceBoneTable.ContainsKey(sourceFlver.Bones[i].Name)) { SourceBoneTable.Add(sourceFlver.Bones[i].Name, i); } } if (meshesRoot == null) { throw new Exception("Could not find Meshes group for this FBX"); } if (bonesRoot == null) { throw new Exception("Could not find Bones group for this FBX"); } //sourceFlver.Bones.Add(targetFlver.Bones[29]); var templateMesh = targetFlver.Meshes.First(); targetFlver.Bones = sourceFlver.Bones; targetFlver.Meshes.Clear(); //targetFlver.SekiroUnk = sourceFlver.SekiroUnk; for (var meshIdx = 0; meshIdx < meshesRoot.transform.childCount; meshIdx++) { // Get the mesh object var meshObj = meshesRoot.transform.GetChild(meshIdx).gameObject; // Get the skin and mesh var meshSkin = meshObj.GetComponent <SkinnedMeshRenderer>(); var bones = meshSkin.bones; var mesh = meshSkin.sharedMesh; // Remap table to recover source bone indices var boneRemap = new int[bones.Length]; for (int i = 0; i < bones.Length; i++) { var name = bones[i].gameObject.name; if (SourceBoneTable.ContainsKey(name)) { boneRemap[i] = SourceBoneTable[name]; } else { boneRemap[i] = 0; } } // Build the submesh's bone table HashSet <int> usedBones = new HashSet <int>(); foreach (var weight in mesh.boneWeights) { if (weight.boneIndex0 >= 0) { usedBones.Add(boneRemap[weight.boneIndex0]); } if (weight.boneIndex1 >= 0) { usedBones.Add(boneRemap[weight.boneIndex1]); } if (weight.boneIndex2 >= 0) { usedBones.Add(boneRemap[weight.boneIndex2]); } if (weight.boneIndex3 >= 0) { usedBones.Add(boneRemap[weight.boneIndex3]); } } // Bad hack for (int i = 0; i < targetFlver.Bones.Count(); i++) { usedBones.Add(i); } var submeshBones = usedBones.OrderBy(x => x).ToArray(); var meshToSubmeshBone = new Dictionary <int, int>(); for (int i = 0; i < submeshBones.Count(); i++) { meshToSubmeshBone.Add(submeshBones[i], i); } // Finally port the mesh to the target //var fmesh = targetFlver.Meshes[0]; var fmesh = new FLVER.Mesh(templateMesh); fmesh.BoneIndices = sourceFlver.Meshes[0].BoneIndices; //submeshBones.ToList(); var min = mesh.bounds.min; var max = mesh.bounds.max; //fmesh.BoundingBoxMax = sourceFlver.Header.BoundingBoxMax; //fmesh.BoundingBoxMin = new System.Numerics.Vector3(max.x*100, max.y*100, max.z*100); //fmesh.BoundingBoxMin = sourceFlver.Header.BoundingBoxMin; //fmesh.MaterialIndex = 0; //targetFlver.Header.BoundingBoxMin = sourceFlver.Header.BoundingBoxMin; //targetFlver.Header.BoundingBoxMax = sourceFlver.Header.BoundingBoxMax; /*foreach (var b in usedBones) * { * targetFlver.Bones[b].Unk3C = 8; * }*/ foreach (var b in targetFlver.Bones) { if (b.Unk3C == 2) { b.Unk3C = 8; } } //targetFlver.Bones[140].Unk3C = 4; //targetFlver.Bones[140].Name = "LG_M_9000"; // Port vertices fmesh.Vertices.Clear(); fmesh.Vertices.Capacity = mesh.vertexCount; var mverts = mesh.vertices; var mnorms = mesh.normals; var mtangs = mesh.tangents; var muvs = mesh.uv; var mbones = mesh.boneWeights; for (int i = 0; i < mesh.vertexCount; i++) { var vert = new FLVER.Vertex(); var pos = mverts[i]; vert.Positions.Add(new System.Numerics.Vector3(pos.x, pos.y, pos.z)); var normal = mnorms[i]; vert.Normals.Add(new System.Numerics.Vector4(-normal.x, -normal.y, -normal.z, -1.0f)); var tangent = mtangs[i]; vert.Tangents.Add(new System.Numerics.Vector4(-tangent.x, -tangent.y, -tangent.z, -tangent.w)); vert.Tangents.Add(new System.Numerics.Vector4(-tangent.x, -tangent.y, -tangent.z, -tangent.w)); var color = new Color32(0xFF, 0xFF, 0x00, 0xFF); //mesh.colors32[i]; vert.Colors.Add(new FLVER.Vertex.Color(color.a, color.r, color.g, color.b)); /*vert.UVs.Add(new System.Numerics.Vector3(0.0f, 0.0f, 0.0f)); * vert.UVs.Add(new System.Numerics.Vector3(0.0f, 0.0f, 0.0f)); * vert.UVs.Add(new System.Numerics.Vector3(0.0f, 0.0f, 0.0f)); * vert.UVs.Add(new System.Numerics.Vector3(0.0f, 0.0f, 0.0f));*/ var uv = muvs[i]; vert.UVs.Add(new System.Numerics.Vector3(uv.x, 1.0f - uv.y, 0.0f)); vert.UVs.Add(new System.Numerics.Vector3(uv.x, 1.0f - uv.y, 0.0f)); vert.UVs.Add(new System.Numerics.Vector3(uv.x, 1.0f - uv.y, 0.0f)); vert.UVs.Add(new System.Numerics.Vector3(uv.x, 1.0f - uv.y, 0.0f)); vert.BoneIndices = new int[4]; vert.BoneWeights = new float[4]; var bone = mbones[i]; //vert.Tangents.Add(new System.Numerics.Vector4(bone.weight0, bone.weight1, bone.weight2, bone.weight3)); vert.BoneWeights[0] = bone.weight0; vert.BoneWeights[1] = bone.weight1; vert.BoneWeights[2] = bone.weight2; vert.BoneWeights[3] = bone.weight3; vert.BoneIndices[0] = meshToSubmeshBone[boneRemap[bone.boneIndex0]]; vert.BoneIndices[1] = meshToSubmeshBone[boneRemap[bone.boneIndex1]]; vert.BoneIndices[2] = meshToSubmeshBone[boneRemap[bone.boneIndex2]]; vert.BoneIndices[3] = meshToSubmeshBone[boneRemap[bone.boneIndex3]]; for (int b = 0; b < 3; b++) { if (vert.BoneIndices[b] == -1) { vert.BoneIndices[b] = 0; } } fmesh.Vertices.Add(vert); } // Port faceset var fset = new FLVER.FaceSet(fmesh.FaceSets[0]); var tris = new uint[mesh.triangles.Count()]; var mtris = mesh.triangles; for (int i = 0; i < tris.Count(); i++) { tris[i] = ((uint)mtris[i]); } fset.Vertices = tris; fset.CullBackfaces = false; fset.TriangleStrip = false; fmesh.FaceSets.Clear(); var fset2 = new FLVER.FaceSet(FLVER.FaceSet.FSFlags.LodLevel1, false, false, fset.Unk06, fset.Unk07, fset.IndexSize, fset.Vertices); var fset3 = new FLVER.FaceSet(FLVER.FaceSet.FSFlags.LodLevel2, false, false, fset.Unk06, fset.Unk07, fset.IndexSize, fset.Vertices); var fset4 = new FLVER.FaceSet(FLVER.FaceSet.FSFlags.Unk80000000, false, false, fset.Unk06, fset.Unk07, fset.IndexSize, fset.Vertices); fmesh.FaceSets.Add(fset); fmesh.FaceSets.Add(fset2); fmesh.FaceSets.Add(fset3); fmesh.FaceSets.Add(fset4); fmesh.MaterialIndex = meshIdx; //fmesh.MaterialIndex = 0; targetFlver.Meshes.Add(fmesh); //targetFlver.BufferLayouts } //targetFlver.Materials[0].MTD = $@"M[ARSN].mtd"; targetFlver.Materials[0].MTD = "C[ARSN].mtd"; targetFlver.Materials[0].Textures[0].Type = "g_DiffuseTexture"; targetFlver.Materials[0].Textures[0].Path = "c5020_shrek_a.dds"; targetFlver.Materials[0].Textures[1].Path = ""; targetFlver.Materials[0].Textures[2].Path = ""; targetFlver.Materials[0].Textures[3].Type = "g_BumpmapTexture"; targetFlver.Materials[0].Textures[3].Path = "SYSTEX_DummyNormal.tga"; targetFlver.Materials.Add(new FLVER.Material("shrek2", "C[ARSN].mtd", targetFlver.Materials[0].Flags, targetFlver.Materials[0].GXBytes)); targetFlver.Materials[1].MTD = "C[ARSN].mtd"; targetFlver.Materials[1].Textures.Add(new FLVER.Texture(targetFlver.Materials[0].Textures[0])); targetFlver.Materials[1].Textures.Add(new FLVER.Texture(targetFlver.Materials[0].Textures[1])); targetFlver.Materials[1].Textures.Add(new FLVER.Texture(targetFlver.Materials[0].Textures[2])); targetFlver.Materials[1].Textures.Add(new FLVER.Texture(targetFlver.Materials[0].Textures[3])); targetFlver.Materials[1].Textures[0].Type = "g_DiffuseTexture"; targetFlver.Materials[1].Textures[0].Path = "c5020_shrekshirt_a.dds"; targetFlver.Materials[1].Textures[1].Path = ""; targetFlver.Materials[1].Textures[2].Path = ""; targetFlver.Materials[1].Textures[3].Type = "g_BumpmapTexture"; targetFlver.Materials[1].Textures[3].Path = "SYSTEX_DummyNormal.tga"; // Finally save targetBnd.Files.Where(x => x.Name.ToUpper().EndsWith(".FLVER")).First().Bytes = targetFlver.Write(); //targetBnd.Write($@"{DarkSoulsTools.ModProjectDirectory}\parts\lg_m_9000.partsbnd.dcx", DCX.Type.SekiroDFLT); targetBnd.Write($@"{DarkSoulsTools.ModProjectDirectory}\chr\c4033.chrbnd.dcx", DCX.Type.DarkSouls3); }
//Current, best version can load FBX, OBJ, DAE etc. //Use assimp library static void importFBX() { AssimpContext importer = new AssimpContext(); // importer.SetConfig(new NormalSmoothingAngleConfig(66.0f)); //m_model.Meshes[0].Bones[0].VertexWeights[0]. var openFileDialog2 = new OpenFileDialog(); string res = ""; if (openFileDialog2.ShowDialog() != DialogResult.OK) { return; } res = openFileDialog2.FileName; //Prepare bone name convertion table: string assemblyPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); string convertStr = File.ReadAllText(assemblyPath + "\\boneConvertion.ini"); //Console.WriteLine("Test reading" + convertStr); string[] convertStrlines = convertStr.Split( new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None ); Dictionary <string, string> convertionTable = new Dictionary <string, string>(); for (int i2 = 0; i2 + 1 < convertStrlines.Length; i2++) { string target = convertStrlines[i2]; if (target == null) { continue; } if (target.IndexOf('#') == 0) { continue; } Console.WriteLine(target + "->" + convertStrlines[i2 + 1]); convertionTable.Add(target, convertStrlines[i2 + 1]); i2++; } //Table prepartion finished Scene md = importer.ImportFile(res, PostProcessSteps.CalculateTangentSpace);// PostProcessPreset.TargetRealTimeMaximumQuality MessageBox.Show("Meshes count:" + md.Meshes.Count + "Material count:" + md.MaterialCount + ""); boneParentList = new Dictionary <String, String>(); //Build the parent list of bones. printNodeStruct(md.RootNode); //First, added a custom default layout. int layoutCount = targetFlver.BufferLayouts.Count; FLVER.BufferLayout newBL = new FLVER.BufferLayout(); newBL.Add(new FLVER.BufferLayout.Member(0, 0, FLVER.BufferLayout.MemberType.Float3, FLVER.BufferLayout.MemberSemantic.Position, 0)); newBL.Add(new FLVER.BufferLayout.Member(0, 12, FLVER.BufferLayout.MemberType.Byte4B, FLVER.BufferLayout.MemberSemantic.Normal, 0)); newBL.Add(new FLVER.BufferLayout.Member(0, 16, FLVER.BufferLayout.MemberType.Byte4B, FLVER.BufferLayout.MemberSemantic.Tangent, 0)); newBL.Add(new FLVER.BufferLayout.Member(0, 20, FLVER.BufferLayout.MemberType.Byte4B, FLVER.BufferLayout.MemberSemantic.Tangent, 1)); newBL.Add(new FLVER.BufferLayout.Member(0, 24, FLVER.BufferLayout.MemberType.Byte4B, FLVER.BufferLayout.MemberSemantic.BoneIndices, 0)); newBL.Add(new FLVER.BufferLayout.Member(0, 28, FLVER.BufferLayout.MemberType.Byte4C, FLVER.BufferLayout.MemberSemantic.BoneWeights, 0)); newBL.Add(new FLVER.BufferLayout.Member(0, 32, FLVER.BufferLayout.MemberType.Byte4C, FLVER.BufferLayout.MemberSemantic.VertexColor, 1)); newBL.Add(new FLVER.BufferLayout.Member(0, 36, FLVER.BufferLayout.MemberType.UVPair, FLVER.BufferLayout.MemberSemantic.UV, 0)); targetFlver.BufferLayouts.Add(newBL); int materialCount = targetFlver.Materials.Count; Boolean flipYZ = false; Boolean setTexture = false; Boolean setAmbientAsDiffuse = false; Boolean setLOD = false; var confirmResult = MessageBox.Show("Do you want to switch YZ axis values? \n It may help importing some fbx files.", "Set", MessageBoxButtons.YesNo); if (confirmResult == DialogResult.Yes) { flipYZ = true; } var confirmResult2 = MessageBox.Show("Auto set texture pathes?", "Set", MessageBoxButtons.YesNo); if (confirmResult2 == DialogResult.Yes) { setTexture = true; /* var res3 = MessageBox.Show("Auto ambient texture as diffuse texture?", * "Set", * MessageBoxButtons.YesNo); * * if (res3 == DialogResult.Yes) * { * setAmbientAsDiffuse = true; * }*/ } var confirmResult3 = MessageBox.Show("Set LOD level? (Only neccssary if this model need to be viewed far away)", "Set", MessageBoxButtons.YesNo); if (confirmResult3 == DialogResult.Yes) { setLOD = true; } foreach (var mat in md.Materials) { FLVER.Material matnew = new JavaScriptSerializer().Deserialize <FLVER.Material>(new JavaScriptSerializer().Serialize(targetFlver.Materials[0])); matnew.Name = res.Substring(res.LastIndexOf('\\') + 1) + "_" + mat.Name; // mat.HasTextureDiffuse if (setTexture) { if (setAmbientAsDiffuse) { if (mat.HasTextureEmissive) { SetFlverMatPath(matnew, "g_DiffuseTexture", FindFileName(mat.TextureEmissive.FilePath) + ".tif"); } } else if (mat.HasTextureDiffuse) //g_DiffuseTexture { // MessageBox.Show("Diffuse mat is" + FindFileName( mat.TextureDiffuse.FilePath)); SetFlverMatPath(matnew, "g_DiffuseTexture", FindFileName(mat.TextureDiffuse.FilePath) + ".tif"); } if (mat.HasTextureNormal)//g_BumpmapTexture { //MessageBox.Show("Diffuse mat is" + FindFileName(mat.TextureNormal.FilePath)); SetFlverMatPath(matnew, "g_BumpmapTexture", FindFileName(mat.TextureNormal.FilePath) + ".tif"); } if (mat.HasTextureSpecular)//g_SpecularTexture { /// MessageBox.Show("Specualr mat is" + FindFileName(mat.TextureSpecular.FilePath)); SetFlverMatPath(matnew, "g_SpecularTexture", FindFileName(mat.TextureSpecular.FilePath) + ".tif"); } } targetFlver.Materials.Add(matnew); } //mn.MaterialIndex = materialCount; foreach (var m in md.Meshes) { /* MessageBox.Show("Name:" + m.Name + "\nHas bones:" + m.HasBones + "\nHas normal:" + m.HasNormals + "\nHas tangent" + m.HasTangentBasis + * "\nVrtices count: " + m.VertexCount * );*/ FLVER.Mesh mn = new FLVER.Mesh(); mn.MaterialIndex = 0; mn.BoneIndices = new List <int>(); mn.BoneIndices.Add(0); mn.BoneIndices.Add(1); mn.BoundingBoxMax = new Vector3(1, 1, 1); mn.BoundingBoxMin = new Vector3(-1, -1, -1); mn.BoundingBoxUnk = new Vector3(); mn.Unk1 = 0; mn.DefaultBoneIndex = 0; mn.Dynamic = true; mn.VertexBuffers = new List <FLVER.VertexBuffer>(); mn.VertexBuffers.Add(new FLVER.VertexBuffer(0, layoutCount, -1)); mn.Vertices = new List <FLVER.Vertex>(); List <List <int> > verticesBoneIndices = new List <List <int> >(); List <List <float> > verticesBoneWeights = new List <List <float> >(); //If it has bones, then record the bone weight info if (m.HasBones) { for (int i2 = 0; i2 < m.VertexCount; i2++) { verticesBoneIndices.Add(new List <int>()); verticesBoneWeights.Add(new List <float>()); } for (int i2 = 0; i2 < m.BoneCount; i2++) { string boneName = m.Bones[i2].Name; int boneIndex = 0; if (convertionTable.ContainsKey(m.Bones[i2].Name)) { boneName = convertionTable[boneName]; // m.Bones[i2].Name = convertionTable[m.Bones[i2].Name]; boneIndex = findFLVER_Bone(targetFlver, boneName); } else { Console.WriteLine("Cannot find ->" + boneName); //If cannot find a corresponding boneName in convertion.ini then //Try to find org bone's parent, check if it boneIndex = findFLVER_Bone(targetFlver, boneName); //if such bone can not be found in flver, then check its parent to see if it can be convert to its parent bone. //check up to 5th grand parent. for (int bp = 0; bp < boneFindParentTimes; bp++) { if (boneIndex == -1) { if (boneParentList.ContainsValue(boneName)) { if (boneParentList[boneName] != null) { boneName = boneParentList[boneName]; if (convertionTable.ContainsKey(boneName)) { boneName = convertionTable[boneName]; } boneIndex = findFLVER_Bone(targetFlver, boneName); } } } } } if (boneIndex == -1) { boneIndex = 0; } for (int i3 = 0; i3 < m.Bones[i2].VertexWeightCount; i3++) { var vw = m.Bones[i2].VertexWeights[i3]; verticesBoneIndices[vw.VertexID].Add(boneIndex); verticesBoneWeights[vw.VertexID].Add(vw.Weight); } } } // m.Bones[0].VertexWeights[0]. for (int i = 0; i < m.Vertices.Count; i++) { var vit = m.Vertices[i]; //m.TextureCoordinateChannels[0] var channels = m.TextureCoordinateChannels[0]; var uv1 = new Vector3D(); var uv2 = new Vector3D(); if (channels != null && m.TextureCoordinateChannelCount > 0) { uv1 = getMyV3D(channels[i]); uv1.Y = 1 - uv1.Y; uv2 = getMyV3D(channels[i]); uv2.Y = 1 - uv2.Y; if (m.TextureCoordinateChannelCount > 1) { // uv2 = getMyV3D((m.TextureCoordinateChannels[1])[i]); } } var normal = new Vector3D(0, 1, 0); if (m.HasNormals && m.Normals.Count > i) { normal = getMyV3D(m.Normals[i]).normalize(); } //Vector3D tangent = new Vector3D( crossPorduct( getMyV3D(m.Tangents[i]).normalize().toXnaV3() , normal.toXnaV3())).normalize(); //var tangent = RotatePoint(normal.toNumV3(), 0, (float)Math.PI / 2, 0); var tangent = new Vector3D(1, 0, 0); if (m.Tangents.Count > i) { tangent = getMyV3D(m.Tangents[i]).normalize(); } else { //Calculate tanget instead if (m.HasNormals && m.Normals.Count > i) { tangent = new Vector3D(crossPorduct(getMyV3D(m.Normals[i]).normalize().toXnaV3(), normal.toXnaV3())).normalize(); } } FLVER.Vertex v = generateVertex(new Vector3(vit.X, vit.Y, vit.Z), uv1.toNumV3(), uv2.toNumV3(), normal.toNumV3(), tangent.toNumV3(), 1); if (flipYZ) { v = generateVertex(new Vector3(vit.X, vit.Z, vit.Y), uv1.toNumV3(), uv2.toNumV3(), new Vector3(normal.X, normal.Z, normal.Y), new Vector3(tangent.X, tangent.Z, tangent.Y), 1); } if (m.HasBones) { for (int j = 0; j < verticesBoneIndices[i].Count && j < 4; j++) { v.BoneIndices[j] = (verticesBoneIndices[i])[j]; v.BoneWeights[j] = (verticesBoneWeights[i])[j]; } } mn.Vertices.Add(v); } List <uint> faceIndexs = new List <uint>(); for (int i = 0; i < m.FaceCount; i++) { if (flipYZ) { if (m.Faces[i].Indices.Count == 3) { faceIndexs.Add((uint)m.Faces[i].Indices[0]); faceIndexs.Add((uint)m.Faces[i].Indices[1]); faceIndexs.Add((uint)m.Faces[i].Indices[2]); } else if (m.Faces[i].Indices.Count == 4) { faceIndexs.Add((uint)m.Faces[i].Indices[0]); faceIndexs.Add((uint)m.Faces[i].Indices[1]); faceIndexs.Add((uint)m.Faces[i].Indices[2]); faceIndexs.Add((uint)m.Faces[i].Indices[2]); faceIndexs.Add((uint)m.Faces[i].Indices[3]); faceIndexs.Add((uint)m.Faces[i].Indices[0]); } } else { if (m.Faces[i].Indices.Count == 3) { faceIndexs.Add((uint)m.Faces[i].Indices[0]); faceIndexs.Add((uint)m.Faces[i].Indices[2]); faceIndexs.Add((uint)m.Faces[i].Indices[1]); } else if (m.Faces[i].Indices.Count == 4) { faceIndexs.Add((uint)m.Faces[i].Indices[0]); faceIndexs.Add((uint)m.Faces[i].Indices[2]); faceIndexs.Add((uint)m.Faces[i].Indices[1]); faceIndexs.Add((uint)m.Faces[i].Indices[2]); faceIndexs.Add((uint)m.Faces[i].Indices[0]); faceIndexs.Add((uint)m.Faces[i].Indices[3]); } } } // mn.FaceSets = new List <FLVER.FaceSet>(); //FLVER.Vertex myv = new FLVER.Vertex(); //myv.Colors = new List<FLVER.Vertex.Color>(); mn.FaceSets.Add(generateBasicFaceSet()); mn.FaceSets[0].Vertices = faceIndexs.ToArray(); if (mn.FaceSets[0].Vertices.Length > 65534) { MessageBox.Show("There are more than 65535 vertices in a mesh , switch to 32 bits index size mode."); mn.FaceSets[0].IndexSize = 32; } if (setLOD == true) { //Special thanks to Meowmaritus { FLVER.FaceSet fs = generateBasicFaceSet(); fs.Flags = SoulsFormats.FLVER.FaceSet.FSFlags.LodLevel1; fs.IndexSize = mn.FaceSets[0].IndexSize; fs.Vertices = (uint[])(mn.FaceSets[0].Vertices.Clone()); mn.FaceSets.Add(fs); } { FLVER.FaceSet fs = generateBasicFaceSet(); fs.Flags = SoulsFormats.FLVER.FaceSet.FSFlags.LodLevel2; fs.IndexSize = mn.FaceSets[0].IndexSize; fs.Vertices = (uint[])(mn.FaceSets[0].Vertices.Clone()); mn.FaceSets.Add(fs); } //unk8000000000 is the motion blur { FLVER.FaceSet fs = generateBasicFaceSet(); fs.Flags = SoulsFormats.FLVER.FaceSet.FSFlags.Unk80000000; fs.IndexSize = mn.FaceSets[0].IndexSize; fs.Vertices = (uint[])(mn.FaceSets[0].Vertices.Clone()); mn.FaceSets.Add(fs); } { FLVER.FaceSet fs = generateBasicFaceSet(); fs.Flags = SoulsFormats.FLVER.FaceSet.FSFlags.LodLevel1 | SoulsFormats.FLVER.FaceSet.FSFlags.Unk80000000; fs.IndexSize = mn.FaceSets[0].IndexSize; fs.Vertices = (uint[])(mn.FaceSets[0].Vertices.Clone()); mn.FaceSets.Add(fs); } { FLVER.FaceSet fs = generateBasicFaceSet(); fs.Flags = SoulsFormats.FLVER.FaceSet.FSFlags.LodLevel2 | SoulsFormats.FLVER.FaceSet.FSFlags.Unk80000000; fs.IndexSize = mn.FaceSets[0].IndexSize; fs.Vertices = (uint[])(mn.FaceSets[0].Vertices.Clone()); mn.FaceSets.Add(fs); } } mn.MaterialIndex = materialCount + m.MaterialIndex; targetFlver.Meshes.Add(mn); } MessageBox.Show("Added a custom mesh! PLease click modify to save it!"); updateVertices(); }
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); }
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 unsafe void FillColorUNorm(byte *dest, ref FLVER.Vertex v) { }
private unsafe void FillBinormalBitangentSNorm8(sbyte *destBinorm, sbyte *destBitan, ref FLVER.Vertex v, byte index) { var tan = v.GetTangent(index); var t = Vector3.Normalize(new Vector3(tan.X, tan.Y, tan.Z)); destBitan[0] = (sbyte)(t.X * 127.0f); destBitan[1] = (sbyte)(t.Y * 127.0f); destBitan[2] = (sbyte)(t.Z * 127.0f); destBitan[3] = (sbyte)(tan.W * 127.0f); var bn = Vector3.Cross(Vector3.Normalize(v.Normal), Vector3.Normalize(new Vector3(t.X, t.Y, t.Z))) * tan.W; destBinorm[0] = (sbyte)(bn.X * 127.0f); destBinorm[1] = (sbyte)(bn.Y * 127.0f); destBinorm[2] = (sbyte)(bn.Z * 127.0f); }
private void FillVertex(ref Vector3 dest, ref FLVER.Vertex v) { dest = v.Position; }
protected override FbxSkin GenerateFbx() { MeshExportData meshData = Souls.meshData.SoulsData; ICollection <BoneIndexToWeightPair> rawBoneDeformerData = new List <BoneIndexToWeightPair>(); for (int vertexIndex = 0; vertexIndex < meshData.mesh.Vertices.Count; ++vertexIndex) { FLVER.Vertex vertex = meshData.mesh.Vertices[vertexIndex]; const int maxVertexDeformations = 4; for (int vertexDeformationIndex = 0; vertexDeformationIndex < maxVertexDeformations; ++vertexDeformationIndex) { BoneIndexToWeightPair weightData = new BoneIndexToWeightPair() { flverBoneIndex = vertex.BoneIndices[vertexDeformationIndex], boneWeight = vertex.BoneWeights[vertexDeformationIndex], vertexIndex = vertexIndex }; if (weightData.flverBoneIndex > 0 && weightData.boneWeight > 0) { rawBoneDeformerData.Add(weightData); } } } foreach (var ddd in rawBoneDeformerData.GroupBy(boneDeformerData => boneDeformerData.vertexIndex).Select(boneDeformedGroup => (vertexIndex: boneDeformedGroup.Key, affectingBonesCount: boneDeformedGroup.Count())).Where((ddd) => ddd.affectingBonesCount > 4)) { System.Console.WriteLine($"Vertex {ddd.vertexIndex} : {ddd.affectingBonesCount}"); } foreach (var ddd in rawBoneDeformerData.GroupBy(boneDeformedData => boneDeformedData.flverBoneIndex).Select(boneDeformerGroup => (boneIndex: boneDeformerGroup.Key, affectingVerticesCount: boneDeformerGroup.Count(), uniqueAffectingVerticesCount: boneDeformerGroup.Select(boneDeformerData => boneDeformerData.vertexIndex).Distinct().Count()))) { if (ddd.affectingVerticesCount != ddd.uniqueAffectingVerticesCount) { System.Console.WriteLine($"Bone {ddd.boneIndex} : vertices {ddd.affectingVerticesCount} : unique {ddd.uniqueAffectingVerticesCount}"); } } FbxSkin skin = FbxSkin.Create(Owner, meshData.meshRoot.Name + "_Skin"); System.Console.WriteLine($"Generating {meshData.meshRoot.Name}"); foreach (var deformerData in rawBoneDeformerData.ToLookup(boneDeformerData => boneDeformerData.flverBoneIndex)) { FLVER2 flver = Souls.flver; FLVER.Bone flverBone = flver.Bones[deformerData.Key]; DsBoneData boneData = Souls.skeleton.boneDatas.Single(boneData => boneData.flverBone == flverBone); //System.Console.WriteLine($"Exporting {deformerData.Key} : {flverBone.Name} with {deformerData.Count(weight=>weight.boneWeight > 0)} vertices"); FbxCluster boneCluster = FbxCluster.Create(skin, meshData.meshRoot.Name + "_" + boneData.exportData.SoulsData.Name + "_Cluster"); boneCluster.SetLink(boneData.exportData.FbxNode); boneCluster.SetLinkMode(FbxCluster.ELinkMode.eTotalOne); boneCluster.SetControlPointIWCount(deformerData.Count()); boneCluster.SetTransformMatrix(Souls.meshData.FbxNode.EvaluateGlobalTransform()); boneCluster.SetTransformLinkMatrix(boneData.exportData.FbxNode.EvaluateGlobalTransform()); foreach (BoneIndexToWeightPair boneWeightPair in deformerData) { boneCluster.AddControlPointIndex(boneWeightPair.vertexIndex, boneWeightPair.boneWeight); //Console.WriteLine("Bone {0} has vertex {1} with weight {2}", flverBone.Name, boneWeightPair.vertexIndex, boneWeightPair.boneWeight); } skin.AddCluster(boneCluster); } //foreach (var dd in rawBoneDeformerData.GroupBy(biwp => biwp.vertexIndex).Where(group => group.Count() > 2)) //{ //System.Console.WriteLine($"Vertex {dd.Key} : {dd.Count()}"); //} //var set = new HashSet<int>(); //for (int vertexIndex = 0; vertexIndex < meshData.mesh.Vertices.Count; ++vertexIndex) //{ // if (!rawBoneDeformerData.Any(x=>x.vertexIndex == vertexIndex)) // { // set.Add(vertexIndex); // } //} //System.Console.WriteLine($"Total {set.Count} unweighted nodes"); return(skin); }
protected override FbxMesh GenerateFbx() { string meshName = (Souls.meshRoot != null ? Souls.meshRoot.Name : "") + "_Mesh"; FbxMesh mesh = FbxMesh.Create(Scene, meshName); mesh.InitControlPoints(Souls.mesh.Vertices.Count); int layerIndex = mesh.CreateLayer(); FbxLayer layer = mesh.GetLayer(layerIndex); FbxLayerContainer layerContainer = FbxLayerContainer.Create(Scene, meshName + "_LayerContainer"); FbxLayerElementUV uv = FbxLayerElementUV.Create(layerContainer, "Diffuse"); layer.SetUVs(uv); FbxLayerElementNormal normal = FbxLayerElementNormal.Create(layerContainer, "Normal"); layer.SetNormals(normal); normal.SetReferenceMode(FbxLayerElement.EReferenceMode.eDirect); normal.SetMappingMode(FbxLayerElement.EMappingMode.eByControlPoint); FbxLayerElementBinormal binormal = FbxLayerElementBinormal.Create(layerContainer, "Binormal"); layer.SetBinormals(binormal); FbxLayerElementTangent tangent = FbxLayerElementTangent.Create(layerContainer, "Tangent"); layer.SetTangents(tangent); tangent.SetReferenceMode(FbxLayerElement.EReferenceMode.eDirect); tangent.SetMappingMode(FbxLayerElement.EMappingMode.eByControlPoint); for (int vertexIndex = 0; vertexIndex < Souls.mesh.Vertices.Count; ++vertexIndex) { FLVER.Vertex vertex = Souls.mesh.Vertices[vertexIndex]; Vector3 position = vertex.Position; // this fixes vertex positions since otherwise the model is turned inside out // and it appears like it holds weapons in the left hand position.Z = -position.Z; normal.GetDirectArray().Add(vertex.Normal.ToFbxVector4()); tangent.GetDirectArray().Add(new FbxVector4(vertex.Tangents[0].X, vertex.Tangents[0].Y, vertex.Tangents[0].Z)); Vector2 uvValue = new Vector2(0); if (vertex.UVs.Count > 0) { uvValue.X = vertex.UVs[0].X; uvValue.Y = vertex.UVs[0].Y; } uv.GetDirectArray().Add(uvValue.ToFbxVector2()); mesh.SetControlPointAt(position.ToFbxVector4(), vertexIndex); } for (int faceSetIndex = 0; faceSetIndex < Souls.mesh.FaceSets.Count; ++faceSetIndex) { FLVER2.FaceSet faceSet = Souls.mesh.FaceSets[faceSetIndex]; if (faceSet.Flags != FLVER2.FaceSet.FSFlags.None) { continue; } for (int faceStartIndex = 0; faceStartIndex < faceSet.Indices.Count; faceStartIndex += 3) { mesh.AddCompletePolygon( faceSet.Indices[faceStartIndex], faceSet.Indices[faceStartIndex + 1], faceSet.Indices[faceStartIndex + 2] ); //mesh.AddCompletePolygon( // faceSet.Indices[faceStartIndex + 2], // faceSet.Indices[faceStartIndex + 1], // faceSet.Indices[faceStartIndex] //); } } mesh.BuildMeshEdgeArray(); FbxGeometryConverter converter = new FbxGeometryConverter(Scene.GetFbxManager()); converter.ComputeEdgeSmoothingFromNormals(mesh); converter.ComputePolygonSmoothingFromEdgeSmoothing(mesh); return(mesh); }