public models Stripify(string filePath) { var stgpv = new Model(); var textures = new MikuMikuLibrary.Textures.TextureSet(); var texdb = new TextureDatabase(); using (var farcArchive = BinaryFile.Load <FarcArchive>(filePath)) { using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txi")).First(), EntryStreamMode.MemoryStream)) texdb.Load(entryStream); using (var entryStream = farcArchive.Open(farcArchive.Entries.Where(c => c.Contains("txd")).First(), EntryStreamMode.MemoryStream)) textures.Load(entryStream); using (var entryStream = farcArchive.Open(farcArchive.Entries.First(), EntryStreamMode.MemoryStream)) stgpv.Load(entryStream, textures, texdb); } foreach (var meshes in stgpv.Meshes) { foreach (var submeshes in meshes.SubMeshes) { foreach (var indexTable in submeshes.IndexTables) { ushort[] triangleStrip = Stripifier.Stripify(indexTable.Indices); if (triangleStrip != null) { indexTable.PrimitiveType = PrimitiveType.TriangleStrip; indexTable.Indices = triangleStrip; } } } } var le_model = new models(); le_model.model = stgpv; le_model.fileName = filePath; Logs.WriteLine("Stripified " + Path.GetFileName(filePath)); return(le_model); }
private static Mesh ConvertMeshFromAiNode(Ai.Node aiNode, Ai.Scene aiScene, Matrix4x4 parentTransformation, Dictionary <string, int> boneMap, List <BoneInfo> bones, Dictionary <string, int> materialMap, List <Material> materials, string texturesDirectory, TextureSet textureSet) { if (!aiNode.HasMeshes) { return(null); } // Select meshes that have triangles var aiMeshes = aiNode.MeshIndices.Select(x => aiScene.Meshes[x]).Where(x => x.PrimitiveType == Ai.PrimitiveType.Triangle && x.Faces.Any(y => y.IndexCount == 3)).ToList(); if (aiMeshes.Count == 0) { return(null); } var transformation = parentTransformation * aiNode.Transform.ToNumericsTransposed(); int vertexCount = aiMeshes.Sum(x => x.VertexCount); var mesh = new Mesh { Name = aiNode.Name, Vertices = new Vector3[vertexCount], }; int vertexOffset = 0; foreach (var aiMesh in aiMeshes) { for (int i = 0; i < aiMesh.Vertices.Count; i++) { mesh.Vertices[vertexOffset + i] = Vector3.Transform(aiMesh.Vertices[i].ToNumerics(), transformation); } if (aiMesh.HasNormals) { if (mesh.Normals == null) { mesh.Normals = new Vector3[vertexCount]; } for (int i = 0; i < aiMesh.Normals.Count; i++) { mesh.Normals[vertexOffset + i] = Vector3.Normalize(Vector3.TransformNormal(aiMesh.Normals[i].ToNumerics(), transformation)); } } if (aiMesh.HasTangentBasis) { if (mesh.Tangents == null) { mesh.Tangents = new Vector4[vertexCount]; } for (int i = 0; i < aiMesh.Tangents.Count; i++) { Vector3 tangent = Vector3.Normalize(Vector3.TransformNormal(aiMesh.Tangents[i].ToNumerics(), transformation)); Vector3 bitangent = Vector3.Normalize(Vector3.TransformNormal(aiMesh.BiTangents[i].ToNumerics(), transformation)); int direction = Math.Sign(Vector3.Dot(bitangent, Vector3.Normalize(Vector3.Cross(mesh.Normals[vertexOffset + i], tangent)))); mesh.Tangents[vertexOffset + i] = new Vector4(tangent, direction); } } if (aiMesh.HasTextureCoords(0)) { if (mesh.UVChannel1 == null) { mesh.UVChannel1 = new Vector2[vertexCount]; } for (int i = 0; i < aiMesh.TextureCoordinateChannels[0].Count; i++) { mesh.UVChannel1[vertexOffset + i] = new Vector2(aiMesh.TextureCoordinateChannels[0][i].X, 1f - aiMesh.TextureCoordinateChannels[0][i].Y); } } if (aiMesh.HasTextureCoords(1)) { if (mesh.UVChannel2 == null) { mesh.UVChannel2 = new Vector2[vertexCount]; } for (int i = 0; i < aiMesh.TextureCoordinateChannels[1].Count; i++) { mesh.UVChannel2[vertexOffset + i] = new Vector2(aiMesh.TextureCoordinateChannels[1][i].X, 1f - aiMesh.TextureCoordinateChannels[1][i].Y); } } if (aiMesh.HasVertexColors(0)) { if (mesh.Colors == null) { mesh.Colors = new Color[vertexCount]; for (int i = 0; i < mesh.Colors.Length; i++) { mesh.Colors[i] = Color.White; } } for (int i = 0; i < aiMesh.VertexColorChannels[0].Count; i++) { mesh.Colors[vertexOffset + i] = new Color(aiMesh.VertexColorChannels[0][i].R, aiMesh.VertexColorChannels[0][i].G, aiMesh.VertexColorChannels[0][i].B, aiMesh.VertexColorChannels[0][i].A); } } var subMesh = new SubMesh(); if (aiMesh.HasBones) { if (mesh.BoneWeights == null) { mesh.BoneWeights = new BoneWeight[vertexCount]; for (int i = 0; i < mesh.BoneWeights.Length; i++) { mesh.BoneWeights[i] = BoneWeight.Empty; } } subMesh.BoneIndices = new ushort[aiMesh.Bones.Count]; for (int i = 0; i < aiMesh.Bones.Count; i++) { var aiBone = aiMesh.Bones[i]; if (!boneMap.TryGetValue(aiBone.Name, out int boneIndex)) { boneIndex = bones.Count; boneMap[aiBone.Name] = boneIndex; bones.Add(ConvertBoneFromAiBone(aiBone, aiScene, boneIndex)); } subMesh.BoneIndices[i] = ( ushort )boneIndex; foreach (var aiWeight in aiBone.VertexWeights) { mesh.BoneWeights[vertexOffset + aiWeight.VertexID].AddWeight(i, aiWeight.Weight); } } } subMesh.Indices = aiMesh.Faces.Where(x => x.IndexCount == 3).SelectMany(x => x.Indices) .Select(x => ( ushort )(vertexOffset + x)).ToArray(); ushort[] triangleStrip = Stripifier.Stripify(subMesh.Indices); if (triangleStrip != null) { subMesh.PrimitiveType = PrimitiveType.TriangleStrip; subMesh.Indices = triangleStrip; } var aiMaterial = aiScene.Materials[aiMesh.MaterialIndex]; if (!materialMap.TryGetValue(aiMaterial.Name, out int materialIndex)) { materialIndex = materials.Count; materialMap[aiMaterial.Name] = materialIndex; materials.Add(ConvertMaterialFromAiMaterial(aiMaterial, texturesDirectory, textureSet)); } subMesh.MaterialIndex = materialIndex; var axisAlignedBoundingBox = new AxisAlignedBoundingBox(mesh.Vertices.Skip(vertexOffset).Take(aiMesh.Vertices.Count)); subMesh.BoundingSphere = axisAlignedBoundingBox.ToBoundingSphere(); subMesh.BoundingBox = axisAlignedBoundingBox.ToBoundingBox(); mesh.SubMeshes.Add(subMesh); vertexOffset += aiMesh.VertexCount; } mesh.BoundingSphere = new AxisAlignedBoundingBox(mesh.Vertices).ToBoundingSphere(); return(mesh); }
protected override void Initialize() { RegisterExportHandler <Model>(filePath => { var configuration = ConfigurationList.Instance.CurrentConfiguration; var objectDatabase = configuration?.ObjectDatabase; var textureDatabase = configuration?.TextureDatabase; var boneDatabase = configuration?.BoneDatabase; Data.Save(filePath, objectDatabase, textureDatabase, boneDatabase); }); RegisterExportHandler <Scene>(filePath => Exporter.ConvertAiSceneFromModel(Data, filePath)); RegisterReplaceHandler <Model>(filePath => { var configuration = ConfigurationList.Instance.CurrentConfiguration; var objectDatabase = configuration?.ObjectDatabase; var textureDatabase = configuration?.TextureDatabase; var model = new Model(); model.Load(filePath, objectDatabase, textureDatabase); return(model); }); RegisterReplaceHandler <Scene>(filePath => { if (Data.Meshes.Count > 1) { return(Importer.ConvertModelFromAiScene(filePath)); } return(Importer.ConvertModelFromAiSceneWithSingleMesh(filePath)); }); RegisterCustomHandler("Rename all shaders to...", () => { using (var inputDialog = new InputDialog { WindowTitle = "Rename all shaders", Input = "BLINN" }) { if (inputDialog.ShowDialog() != DialogResult.OK) { return; } foreach (var material in Data.Meshes.SelectMany(x => x.Materials)) { material.Shader = inputDialog.Input; } } IsDirty = true; }); RegisterCustomHandler("Convert triangles to triangle strips", () => { foreach (var indexTable in Data.Meshes.SelectMany(x => x.SubMeshes).SelectMany(x => x.IndexTables)) { if (indexTable.PrimitiveType == PrimitiveType.Triangles) { ushort[] triangleStrip = Stripifier.Stripify(indexTable.Indices); if (triangleStrip != null) { indexTable.PrimitiveType = PrimitiveType.TriangleStrip; indexTable.Indices = triangleStrip; } } } IsDirty = true; }); RegisterCustomHandler("Combine all meshes into one", () => { if (Data.Meshes.Count <= 1) { return; } var combinedMesh = new Mesh { Name = "Combined mesh" }; var indexMap = new Dictionary <int, int>(); foreach (var mesh in Data.Meshes) { if (mesh.Skin != null) { if (combinedMesh.Skin == null) { combinedMesh.Skin = new Skin(); combinedMesh.Skin.Bones.AddRange(mesh.Skin.Bones); } else { for (int i = 0; i < mesh.Skin.Bones.Count; i++) { var bone = mesh.Skin.Bones[i]; var boneIndex = combinedMesh.Skin.Bones .FindIndex(x => x.Name.Equals(bone.Name, StringComparison.OrdinalIgnoreCase)); if (boneIndex == -1) { indexMap[i] = combinedMesh.Skin.Bones.Count; combinedMesh.Skin.Bones.Add(bone); } else { indexMap[i] = boneIndex; } } foreach (var indexTable in mesh.SubMeshes.SelectMany(x => x.IndexTables)) { if (indexTable.BoneIndices?.Length >= 1) { for (int i = 0; i < indexTable.BoneIndices.Length; i++) { indexTable.BoneIndices[i] = ( ushort )indexMap[indexTable.BoneIndices[i]]; } } } } } foreach (var indexTable in mesh.SubMeshes.SelectMany(x => x.IndexTables)) { indexTable.MaterialIndex += combinedMesh.Materials.Count; } combinedMesh.SubMeshes.AddRange(mesh.SubMeshes); combinedMesh.Materials.AddRange(mesh.Materials); } Data.Meshes.Clear(); Data.Meshes.Add(combinedMesh); if (IsPopulated) { IsPopulated = false; Populate(); } IsDirty = true; }); base.Initialize(); }
/// <summary> /// Generates strips from the given input indices. /// </summary> /// <param name="indices">Input index list, the indices you would use to render.</param> /// <param name="primitiveGroups">Array of optimized/stripified PrimitiveGroups</param> /// <param name="validateEnabled">Whether to validate the output or not.</param> /// <returns>A boolean indicating whether the operation completed successfully.</returns> public bool GenerateStrips(ushort[] indices, out PrimitiveGroup[] primitiveGroups, bool validateEnabled = false) { var numGroups = 0; //put data in format that the stripifier likes var tempIndices = new List <ushort>(indices.Length); ushort maxIndex = 0; ushort minIndex = 0xFFFF; for (var i = 0; i < indices.Length; i++) { tempIndices.Add(indices[i]); if (indices[i] > maxIndex) { maxIndex = indices[i]; } if (indices[i] < minIndex) { minIndex = indices[i]; } } var tempStrips = new List <StripInfo>(); var tempFaces = new List <FaceInfo>(); var stripifier = new Stripifier(); //do actual stripification stripifier.Stripify(tempIndices, CacheSize, MinStripSize, maxIndex, tempStrips, tempFaces); //stitch strips together var stripIndices = new List <int>(); uint numSeparateStrips = 0; if (ListsOnly) { //if we're outputting only lists, we're done numGroups = 1; primitiveGroups = new PrimitiveGroup[numGroups]; var primGroupArray = primitiveGroups; //count the total number of indices uint numIndices = 0; for (var i = 0; i < tempStrips.Count; i++) { numIndices += ( uint )(tempStrips[i].Faces.Count * 3); } //add in the list numIndices += ( uint )(tempFaces.Count * 3); primGroupArray[0] = new PrimitiveGroup(PrimitiveType.TriangleList, new ushort[numIndices]); //do strips uint indexCtr = 0; for (var i = 0; i < tempStrips.Count; i++) { for (var j = 0; j < tempStrips[i].Faces.Count; j++) { //degenerates are of no use with lists if (!Stripifier.IsDegenerate(tempStrips[i].Faces[j])) { primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V0; primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V1; primGroupArray[0].Indices[indexCtr++] = ( ushort )tempStrips[i].Faces[j].V2; } else { //we've removed a tri, reduce the number of indices var resizedIndices = primGroupArray[0].Indices; Array.Resize(ref resizedIndices, primGroupArray[0].Indices.Length - 3); primGroupArray[0].Indices = resizedIndices; } } } //do lists for (var i = 0; i < tempFaces.Count; i++) { primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V0; primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V1; primGroupArray[0].Indices[indexCtr++] = ( ushort )tempFaces[i].V2; } } else { stripifier.CreateStrips(tempStrips, stripIndices, StitchStrips, ref numSeparateStrips, UseRestart, ( uint )RestartValue); //if we're stitching strips together, we better get back only one strip from CreateStrips() Debug.Assert(StitchStrips && numSeparateStrips == 1 || !StitchStrips); //convert to output format numGroups = ( ushort )numSeparateStrips; //for the strips if (tempFaces.Count != 0) { numGroups++; //we've got a list as well, increment } primitiveGroups = new PrimitiveGroup[numGroups]; var primGroupArray = primitiveGroups; //first, the strips var startingLoc = 0; for (var stripCtr = 0; stripCtr < numSeparateStrips; stripCtr++) { var stripLength = 0; if (!StitchStrips) { //if we've got multiple strips, we need to figure out the correct length int i; for (i = startingLoc; i < stripIndices.Count; i++) { if (stripIndices[i] == -1) { break; } } stripLength = i - startingLoc; } else { stripLength = stripIndices.Count; } primGroupArray[stripCtr] = new PrimitiveGroup(PrimitiveType.TriangleStrip, new ushort[stripLength]); var indexCtr = 0; for (int i = startingLoc; i < stripLength + startingLoc; i++) { primGroupArray[stripCtr].Indices[indexCtr++] = ( ushort )stripIndices[i]; } //we add 1 to account for the -1 separating strips //this doesn't break the stitched case since we'll exit the loop startingLoc += stripLength + 1; } //next, the list if (tempFaces.Count != 0) { int faceGroupLoc = numGroups - 1; //the face group is the last one primGroupArray[faceGroupLoc] = new PrimitiveGroup(PrimitiveType.TriangleList, new ushort[tempFaces.Count * 3]); var indexCtr = 0; for (var i = 0; i < tempFaces.Count; i++) { primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V0; primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V1; primGroupArray[faceGroupLoc].Indices[indexCtr++] = ( ushort )tempFaces[i].V2; } } } //validate generated data against input if (validateEnabled) { var numbins = 100; var inBins = new List <FaceInfo> [numbins]; for (var i = 0; i < inBins.Length; ++i) { inBins[i] = new List <FaceInfo>(); } //hash input indices on first index for (var i = 0; i < indices.Length; i += 3) { var faceInfo = new FaceInfo(indices[i], indices[i + 1], indices[i + 2]); inBins[indices[i] % numbins].Add(faceInfo); } for (var i = 0; i < numGroups; ++i) { switch (primitiveGroups[i].Type) { case PrimitiveType.TriangleList: { for (var j = 0; j < primitiveGroups[i].Indices.Length; j += 3) { ushort v0 = primitiveGroups[i].Indices[j]; ushort v1 = primitiveGroups[i].Indices[j + 1]; ushort v2 = primitiveGroups[i].Indices[j + 2]; //ignore degenerates if (Stripifier.IsDegenerate(v0, v1, v2)) { continue; } if (!TestTriangle(v0, v1, v2, inBins, numbins)) { Cleanup(tempStrips, tempFaces); return(false); } } break; } case PrimitiveType.TriangleStrip: { var flip = false; for (var j = 2; j < primitiveGroups[i].Indices.Length; ++j) { ushort v0 = primitiveGroups[i].Indices[j - 2]; ushort v1 = primitiveGroups[i].Indices[j - 1]; ushort v2 = primitiveGroups[i].Indices[j]; if (flip) { //swap v1 and v2 ushort swap = v1; v1 = v2; v2 = swap; } //ignore degenerates if (Stripifier.IsDegenerate(v0, v1, v2)) { flip = !flip; continue; } if (!TestTriangle(v0, v1, v2, inBins, numbins)) { Cleanup(tempStrips, tempFaces); return(false); } flip = !flip; } break; } case PrimitiveType.TriangleFan: default: break; } } } //clean up everything Cleanup(tempStrips, tempFaces); return(true); }