public static IEnumerable <BigMesh> Split(BigMesh bigMesh) { var stripIndex = 0; var maxStrip = bigMesh.triangleStripList.Count; while (stripIndex < maxStrip) { var smallMesh = new BigMesh { textureIndex = bigMesh.textureIndex, matDef = bigMesh.matDef, }; var localIndexMap = new Dictionary <int, int>(); var countStocked = 0; for (; stripIndex < maxStrip; stripIndex++) { var triStrip = bigMesh.triangleStripList[stripIndex]; if (countStocked + triStrip.vertexIndices.Count > MaxVertCount) { break; } var newTriStrip = new BigMesh.TriangleStrip { uvList = triStrip.uvList.ToList(), vertexColorList = triStrip.vertexColorList.ToList(), vertexIndices = triStrip.vertexIndices .Select(gi => MapToLocal(localIndexMap, gi)) .ToList(), }; smallMesh.triangleStripList.Add(newTriStrip); countStocked += newTriStrip.vertexIndices.Count; } var globalIndexMap = localIndexMap .ToDictionary(pair => pair.Value, pair => pair.Key); smallMesh.textureIndex = bigMesh.textureIndex; for (int loop = 0; loop < localIndexMap.Count; loop++) { var globalIndex = globalIndexMap[loop]; smallMesh.vertexList.Add(bigMesh.vertexList[globalIndex]); } yield return(smallMesh); } }
private void ConvertModelIntoMapModel(string modelFile, MapGenConfig config) { logger.Debug($"Loading 3D model file \"{modelFile}\" using Assimp."); var assimp = new Assimp.AssimpContext(); var scene = assimp.ImportFile(modelFile, Assimp.PostProcessSteps.PreTransformVertices); bigMeshContainer = new BigMeshContainer(); var scale = config.scale; Matrix4x4 matrix = Matrix4x4.Identity; if (config.applyMatrix != null) { var m = config.applyMatrix; if (m.Length == 16) { matrix = new Matrix4x4( m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15] ); logger.Debug($"Apply matrix: {matrix}"); } } else { matrix *= scale; } logger.Debug($"Starting triangle strip conversion for {scene.Meshes.Count} meshes."); foreach (var inputMesh in scene.Meshes) { logger.Debug($"Mesh: {inputMesh.Name} ({inputMesh.FaceCount:#,##0} faces, {inputMesh.VertexCount:#,##0} vertices)"); var modelMat = scene.Materials[inputMesh.MaterialIndex]; var matDef = config.FindMaterial(modelMat.Name ?? "default") ?? MaterialDef.CreateFallbackFor(modelMat.Name); if (matDef.ignore) { logger.Info($"This mesh \"{inputMesh.Name}\" is not rendered due to ignore flag of material \"{modelMat.Name}\"."); continue; } var kh2Mesh = bigMeshContainer.AllocateBigMeshForMaterial(matDef); var diffuseTextureFile = modelMat.TextureDiffuse.FilePath; if (!string.IsNullOrEmpty(diffuseTextureFile)) { if (config.reuseImd) { logger.Debug($"The mesh \"{inputMesh.Name}\" material \"{matDef.name}\" has filepath \"{diffuseTextureFile}\" for diffuse texture. It will be associated with material's fromFile3. Setting preferable imd file to fromFile2 due to reuseImd flag."); matDef.fromFile2 = Path.ChangeExtension(diffuseTextureFile, ".imd"); matDef.fromFile3 = diffuseTextureFile; } else { logger.Debug($"The mesh \"{inputMesh.Name}\" material \"{matDef.name}\" has filepath \"{diffuseTextureFile}\" for diffuse texture. It will be associated with material's fromFile2."); matDef.fromFile3 = diffuseTextureFile; } } var kh2BaseVert = kh2Mesh.vertexList.Count; List <int> vertexToLocal = new List <int>(); foreach (var inputVertex in inputMesh.Vertices) { var vertex = Vector3.Transform( new Vector3(inputVertex.X, inputVertex.Y, inputVertex.Z), matrix ); var index = kh2Mesh.vertexList.IndexOf(vertex); if (index < 0) { index = kh2Mesh.vertexList.Count; kh2Mesh.vertexList.Add(vertex); } vertexToLocal.Add(index); } var localFaces = inputMesh.Faces .Select( set => set.Indices .Select(index => new VertPair { uvColorIndex = index, vertexIndex = vertexToLocal[index] }) .ToArray() ) .ToArray(); var inputTexCoords = inputMesh.TextureCoordinateChannels.First(); var inputVertexColorList = inputMesh.VertexColorChannels.First(); var hasVertexColor = inputMesh.VertexColorChannelCount >= 1; var maxIntensity = matDef.maxColorIntensity ?? config.maxColorIntensity ?? 128; var maxAlpha = matDef.maxAlpha ?? config.maxAlpha ?? 128; var triConverter = config.disableTriangleStripsOptimization ? (TriangleFansToTriangleStripsConverter)TriangleFansToTriangleStripsNoOpts : (TriangleFansToTriangleStripsConverter)TriangleFansToTriangleStripsOptimized; foreach (var triStripInput in triConverter(localFaces)) { var triStripOut = new BigMesh.TriangleStrip(); foreach (var vertPair in triStripInput) { triStripOut.vertexIndices.Add(kh2BaseVert + vertPair.vertexIndex); triStripOut.uvList.Add(Get2DCoord(inputTexCoords[vertPair.uvColorIndex])); if (hasVertexColor) { triStripOut.vertexColorList.Add(ConvertVertexColor(inputVertexColorList[vertPair.uvColorIndex], maxIntensity, maxAlpha)); } else { triStripOut.vertexColorList.Add(new Color(maxIntensity, maxIntensity, maxIntensity, maxAlpha)); } } kh2Mesh.triangleStripList.Add(triStripOut); } logger.Debug($"Output: {kh2Mesh.vertexList.Count:#,##0} vertices, {kh2Mesh.triangleStripList.Count:#,##0} triangle strips."); } logger.Debug($"The conversion has done."); logger.Debug($"Starting mesh splitter and vif packets builder."); mapModel = new Mdlx.M4 { VifPackets = new List <Mdlx.VifPacketDescriptor>(), }; foreach (var bigMesh in bigMeshContainer.MeshList .Where(it => it.textureIndex != -1) ) { foreach (var smallMesh in BigMeshSplitter.Split(bigMesh)) { var dmaPack = new MapVifPacketBuilder(smallMesh); smallMeshList.Add(smallMesh); bigMesh.vifPacketIndices.Add(Convert.ToUInt16(mapModel.VifPackets.Count)); smallMesh.vifPacketIndices.Add(Convert.ToUInt16(mapModel.VifPackets.Count)); mapModel.VifPackets.Add( new Mdlx.VifPacketDescriptor { VifPacket = dmaPack.vifPacket.ToArray(), TextureId = smallMesh.textureIndex, DmaPerVif = new ushort[] { dmaPack.firstVifPacketQwc, 0, }, IsTransparentFlag = smallMesh.matDef.transparentFlag ?? 0, } ); } } logger.Debug($"Output: {mapModel.VifPackets.Count:#,##0} vif packets."); logger.Debug($"The builder has done."); logger.Debug($"Starting vifPacketRenderingGroup builder."); // first group: render all mapModel.vifPacketRenderingGroup = new List <ushort[]>( new ushort[][] { Enumerable.Range(0, mapModel.VifPackets.Count) .Select(it => Convert.ToUInt16(it)) .ToArray() } ); logger.Debug($"Output: {mapModel.vifPacketRenderingGroup.Count:#,##0} groups."); mapModel.DmaChainIndexRemapTable = new List <ushort>( Enumerable.Range(0, mapModel.VifPackets.Count) .Select(it => Convert.ToUInt16(it)) .ToArray() ); }