示例#1
0
/*public static void WriteToStreamFromRawDxt5a(
 *  Stream dst,
 *  byte[] src,
 *  int srcOffset,
 *  int width,
 *  int height) {
 * var ew = new EndianBinaryWriter(dst, Endianness.LittleEndian);
 *
 * var imageSize = width * height / 2;
 *
 * ew.Write("DDS ", Encoding.ASCII, false);
 * ew.Write(124);
 * ew.Write(0x000A1007);
 * ew.Write(width);
 * ew.Write(height);
 * ew.Write(imageSize);
 * ew.Write(0);
 * ew.Write(1);
 *
 * for (var i = 0; i < 11; ++i) {
 *  ew.Write(0);
 * }
 *
 * ew.Write(0x20);
 * ew.Write(4);
 * ew.Write("ATI1", Encoding.ASCII, false);
 * ew.Write(0);
 * ew.Write(0);
 * ew.Write(0);
 * ew.Write(0);
 * ew.Write(0);
 * ew.Write(0x401008);
 *
 * var fixedBuffer = new byte[imageSize];
 * for (var i = 0; i < imageSize; i += 8) {
 *  fixedBuffer[i + 0] = src[srcOffset + i + 1];
 *  fixedBuffer[i + 1] = src[srcOffset + i + 0];
 *
 *  fixedBuffer[i + 2] = src[srcOffset + i + 3];
 *  fixedBuffer[i + 3] = src[srcOffset + i + 2];
 *  fixedBuffer[i + 4] = src[srcOffset + i + 5];
 *
 *  fixedBuffer[i + 5] = src[srcOffset + i + 4];
 *  fixedBuffer[i + 6] = src[srcOffset + i + 7];
 *  fixedBuffer[i + 7] = src[srcOffset + i + 6];
 * }
 *
 * ew.Position = 128;
 * ew.Write(fixedBuffer, 0, imageSize);
 * Asserts.Equal(128 + imageSize, ew.Position);
 * }*/

        public static unsafe IImage DecompressDxt5a(
            byte[] src,
            int srcOffset,
            int width,
            int height)
        {
            const int blockSize   = 4;
            var       blockCountX = width / blockSize;
            var       blockCountY = height / blockSize;

            var imageSize = width * height / 2;

            var monoTable = new byte[8];
            var rIndices  = new byte[16];

            // TODO: Support grayscale?
            var bitmap = new I8Image(width, height);

            bitmap.Mutate((_, setHandler) => {
                for (var i = 0; i < imageSize; i += 8)
                {
                    var iOff = srcOffset + i;

                    // Gathers up color palette.
                    var mono0 = monoTable[0] = src[iOff + 0];
                    var mono1 = monoTable[1] = src[iOff + 1];

                    var useEightIndexMode = mono0 > mono1;
                    if (useEightIndexMode)
                    {
                        monoTable[2] = (byte)((6 * mono0 + 1 * mono1) / 7f);
                        monoTable[3] = (byte)((5 * mono0 + 2 * mono1) / 7f);
                        monoTable[4] = (byte)((4 * mono0 + 3 * mono1) / 7f);
                        monoTable[5] = (byte)((3 * mono0 + 4 * mono1) / 7f);
                        monoTable[6] = (byte)((2 * mono0 + 5 * mono1) / 7f);
                        monoTable[7] = (byte)((1 * mono0 + 6 * mono1) / 7f);
                    }
                    else
                    {
                        monoTable[2] = (byte)((4 * mono0 + 1 * mono1) / 5f);
                        monoTable[3] = (byte)((3 * mono0 + 2 * mono1) / 5f);
                        monoTable[4] = (byte)((2 * mono0 + 3 * mono1) / 5f);
                        monoTable[5] = (byte)((1 * mono0 + 4 * mono1) / 5f);
                        monoTable[6] = 0;
                        monoTable[7] = 255;
                    }

                    // Gathers up color indices.
                    ulong indices = 0;
                    for (var b = 0; b < 6; ++b)
                    {
                        ulong part = src[iOff + 2 + b];
                        part     <<= (8 * b);
                        indices   |= part;
                    }

                    for (var ii = 0; ii < 16; ++ii)
                    {
                        rIndices[ii] = (byte)(indices & 7);
                        indices    >>= 3;
                    }

                    // Writes pixels to output image.
                    // TODO: This might actually be flipped across the X/Y axis. This is
                    // kept this way to align with the albedo texture for now.
                    var tileIndex = i / 8;
                    var tileY     = tileIndex % blockCountY;
                    var tileX     = (tileIndex - tileY) / blockCountX;

                    for (var j = 0; j < blockSize; j++)
                    {
                        for (var k = 0; k < blockSize; k++)
                        {
                            var value = monoTable[rIndices[(j * blockSize) + k]];

                            var outX = (tileX * blockSize) + j;
                            var outY = tileY * blockSize + k;

                            setHandler(outX, outY, value);
                        }
                    }
                }
            });

            return(bitmap);
        }
        public IModel LoadModel(OutModelFileBundle modelFileBundle)
        {
            var outFile = modelFileBundle.OutFile;

            var isBw2 = modelFileBundle.GameVersion == GameVersion.BW2;

            Stream stream;

            if (isBw2)
            {
                using var gZipStream =
                          new GZipStream(outFile.Impl.OpenRead(),
                                         CompressionMode.Decompress);

                stream = new MemoryStream();
                gZipStream.CopyTo(stream);
                stream.Position = 0;
            }
            else
            {
                stream = outFile.Impl.OpenRead();
            }

            using var er = new EndianBinaryReader(stream, Endianness.LittleEndian);

            var bwHeightmap =
                isBw2
              ? (IBwHeightmap)er.ReadNew <Bw2Heightmap>()
              : er.ReadNew <Bw1Heightmap>();

            var finModel = new ModelImpl();

            var finSkin = finModel.Skin;
            var finMesh = finSkin.AddMesh();

            var triangles = new List <(IVertex, IVertex, IVertex)>();

            var chunks = bwHeightmap.Chunks;

            var heightmapWidth   = 64 * 4 * 4;
            var heightmapHeight  = 64 * 4 * 4;
            var chunkFinVertices =
                new Grid <IVertex?>(heightmapWidth, heightmapHeight);

            var heights = new Grid <ushort>(heightmapWidth, heightmapHeight);

            for (var chunkY = 0; chunkY < chunks.Height; ++chunkY)
            {
                for (var chunkX = 0; chunkX < chunks.Width; ++chunkX)
                {
                    var tiles = chunks[chunkX, chunkY]?.Tiles;
                    if (tiles == null)
                    {
                        continue;
                    }

                    for (var tileY = 0; tileY < tiles.Height; ++tileY)
                    {
                        for (var tileX = 0; tileX < tiles.Width; ++tileX)
                        {
                            var points = tiles[tileX, tileY].Points;

                            for (var pointY = 0; pointY < points.Height; ++pointY)
                            {
                                for (var pointX = 0; pointX < points.Width; ++pointX)
                                {
                                    var point = points[pointX, pointY];

                                    var heightmapX = 16 * chunkX + 4 * tileX + pointX;
                                    var heightmapY = 16 * chunkY + 4 * tileY + pointY;

                                    var finVertex =
                                        finSkin.AddVertex(point.X, point.Height, point.Y)
                                        .SetUv(1f * heightmapX / heightmapWidth,
                                               1f * heightmapY / heightmapHeight);

                                    chunkFinVertices[heightmapX, heightmapY] = finVertex;
                                    heights[heightmapX, heightmapY]          = point.Height;
                                }
                            }
                        }
                    }
                }
            }

            var image = new I8Image(heightmapWidth, heightmapHeight);

            image.Mutate((_, setHandler) => {
                for (var vY = 0; vY < heightmapHeight; ++vY)
                {
                    for (var vX = 0; vX < heightmapWidth; ++vX)
                    {
                        setHandler(vX, vY, (byte)(heights[vX, vY] / 24));
                    }
                }
            });
            var heightmapTexture = finModel.MaterialManager.CreateTexture(image);
            var finMaterial      =
                finModel.MaterialManager.AddTextureMaterial(heightmapTexture);

            for (var vY = 0; vY < heightmapHeight - 1; ++vY)
            {
                for (var vX = 0; vX < heightmapWidth - 1; ++vX)
                {
                    var a = chunkFinVertices[vX, vY];
                    var b = chunkFinVertices[vX + 1, vY];
                    var c = chunkFinVertices[vX, vY + 1];
                    var d = chunkFinVertices[vX + 1, vY + 1];

                    if (a != null && b != null && c != null && d != null)
                    {
                        triangles.Add((a, b, c));
                        triangles.Add((d, c, b));
                    }
                }
            }

            finMesh.AddTriangles(triangles.ToArray()).SetMaterial(finMaterial);

            return(finModel);
        }