Example #1
0
 private IEnumerable <Bar.Entry> LoadAdditionalBarEntries(MapGenConfig config, Func <string, byte[]> fileLoader)
 {
     foreach (var addFile in config.addFiles)
     {
         var data = fileLoader(addFile.fromFile);
         yield return(new Bar.Entry
         {
             Name = addFile.name,
             Type = (Bar.EntryType)addFile.type,
             Stream = new MemoryStream(data),
             Index = addFile.index,
         });
     }
 }
Example #2
0
        private Imgd FileLoader(string filePath, MaterialDef matDef, MapGenConfig config)
        {
            logger.Debug($"Load image from \"{filePath}\"");

            if (FileExtUtil.IsExtension(filePath, ".imd"))
            {
                return(ImageResizer.NormalizeImageSize(File.OpenRead(filePath).Using(s => Imgd.Read(s))));
            }
            if (FileExtUtil.IsExtension(filePath, ".png"))
            {
                var imdFile = Path.ChangeExtension(filePath, ".imd");

                if (config.skipConversionIfExists && File.Exists(imdFile))
                {
                    logger.Debug($"Skipping png to imd conversion, due to imd file existence and skipConversionIfExists option.");
                }
                else
                {
                    try
                    {
                        logger.Debug($"Using ImgTool for png to imd conversion.");

                        var imgtoolOptions = matDef.imgtoolOptions ?? config.imgtoolOptions ?? "-b 8";

                        var result = new RunCmd(
                            "OpenKh.Command.ImgTool.exe",
                            $"imd \"{filePath}\" -o \"{imdFile}\" {imgtoolOptions}"
                            );

                        if (result.ExitCode != 0)
                        {
                            throw new Exception($"ImgTool failed ({result.ExitCode})");
                        }
                    }
                    catch (Win32Exception ex)
                    {
                        throw new Exception("ImgTool failed.", ex);
                    }
                }

                return(FileLoader(imdFile, matDef, config));
            }

            throw new NotSupportedException(Path.GetExtension(filePath));
        }
Example #3
0
        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()
                );
        }
Example #4
0
        public MapBuilder(string modelFile, MapGenConfig config, Func <MaterialDef, Imgd> imageLoader)
        {
            this.config = config;

            ConvertModelIntoMapModel(modelFile, config);

            logger.Debug($"Starting collision plane builder.");

            collisionBuilder = new CollisionBuilder(smallMeshList, config.disableBSPCollisionBuilder);

            if (collisionBuilder.vifPacketRenderingGroup != null)
            {
                logger.Debug($"Updating vifPacketRenderingGroup builder.");

                mapModel.vifPacketRenderingGroup = collisionBuilder.vifPacketRenderingGroup;

                logger.Debug($"Output: {mapModel.vifPacketRenderingGroup.Count:#,##0} groups.");
            }

            logger.Debug($"Output: {collisionBuilder.coct.CollisionMeshGroupList.Count:#,##0} collision mesh groups");

            {
                var matDefList = bigMeshContainer.AllocatedMaterialDefs;
                var imageSets  = matDefList
                                 .Select(matDef => new ImageSet {
                    image = imageLoader(matDef), matDef = matDef,
                })
                                 .ToArray();

                var build = new ModelTexture.Build
                {
                    images = imageSets
                             .Select(set => set.image)
                             .ToArray(),

                    offsetData = null,      // auto serial number map

                    textureTransfer = null, // auto create

                    gsInfo = imageSets
                             .Select(
                        set =>
                    {
                        var gsInfo = new ModelTexture.UserGsInfo(set.image);
                        gsInfo.AddressMode.AddressU =
                            Enum.Parse <ModelTexture.TextureWrapMode>(
                                set.matDef.textureOptions.addressU
                                ?? config.textureOptions.addressU
                                ?? "Repeat"
                                );
                        gsInfo.AddressMode.AddressV =
                            Enum.Parse <ModelTexture.TextureWrapMode>(
                                set.matDef.textureOptions.addressV
                                ?? config.textureOptions.addressV
                                ?? "Repeat"
                                );
                        return(gsInfo);
                    }
                        )
                             .ToArray(),
                };

                modelTex = new ModelTexture(build);
            }
        }
Example #5
0
        private Func <MaterialDef, Imgd> CreateImageLoader(string baseDir, MapGenConfig config, Func <string, MaterialDef, MapGenConfig, Imgd> fileLoader)
        {
            return((matDef) =>
            {
                logger.Debug($"Going to load material \"{matDef.name}\".");

                var fileNames = new string[] {
                    matDef.fromFile,
                    matDef.fromFile2,
                    matDef.fromFile3,
                    matDef.name + ".imd",
                    matDef.name + ".png",
                }
                .Where(it => !string.IsNullOrWhiteSpace(it))
                .ToList();

                var allImageDirs = new string[] { }
                .Concat(
                    (config.imageDirs ?? new string[0])
                    .Select(imageDir => Path.Combine(baseDir, imageDir))
                    )
                .Concat(
                    new string[] { baseDir }
                    )
                .ToArray();

                var pathList =
                    allImageDirs
                    .SelectMany(
                        imageDir => fileNames.Select(fileName => Path.Combine(imageDir, fileName))
                        )
                    .ToArray();

                var found = pathList
                            .FirstOrDefault(File.Exists);

                Imgd loaded = null;

                if (found != null && File.Exists(found))
                {
                    try
                    {
                        loaded = fileLoader(found, matDef, config);
                    }
                    catch (Exception ex)
                    {
                        logger.Warn(ex, "Load image failed!");
                    }
                }

                if (loaded == null)
                {
                    logger.Warn($"File not found of material \"{matDef.name}\", or error thrown. Using fallback image instead.");

                    // fallback
                    var pixels = new byte[128 * 128];
                    for (int x = 0; x < pixels.Length; x++)
                    {
                        var y = x / 128;
                        var set = 0 != ((1 & (x / 8)) ^ (1 & (y / 8)));
                        pixels[x] = (byte)(set ? 255 : 95);
                    }

                    var palette = new byte[4 * 256];
                    for (int x = 0; x < 256; x++)
                    {
                        var v = (byte)x;
                        palette[4 * x + 0] = v;
                        palette[4 * x + 1] = v;
                        palette[4 * x + 2] = v;
                        palette[4 * x + 3] = 255;
                    }

                    return Imgd.Create(
                        new Size(128, 128),
                        Imaging.PixelFormat.Indexed8,
                        pixels,
                        palette,
                        false
                        );
                }

                return loaded;
            });
        }