Example #1
0
        /// <summary>
        /// Decode the provided png file to a BGRA pixel array, Y flipped.
        /// </summary>
        /// <param name="pngData">The png file as bytes.</param>
        /// <param name="fileHeader">The png file's header.</param>
        /// <returns>The RGBA pixel data from the png.</returns>
        public static byte[] Decode(ReadOnlyMemory <byte> pngData, out PngFileHeader fileHeader)
        {
            fileHeader = new PngFileHeader();
            if (!IsPng(pngData))
            {
                Engine.Log.Warning("Tried to decode a non-png image!", MessageSource.ImagePng);
                return(null);
            }

            using var stream = new ByteReader(pngData);
            stream.Seek(8, SeekOrigin.Current); // Increment by header bytes.

            // Read chunks while there are valid chunks.
            var      dataStream = new ReadOnlyLinkedMemoryStream();
            PngChunk currentChunk;
            var      endChunkReached = false;
            ReadOnlyMemory <byte> palette = null, paletteAlpha = null;
            int width = 0, height = 0;

            while ((currentChunk = new PngChunk(stream)).Valid)
            {
                if (endChunkReached)
                {
                    Engine.Log.Warning("Image did not end with an end chunk...", MessageSource.ImagePng);
                    continue;
                }

                switch (currentChunk.Type)
                {
                case PngChunkTypes.HEADER:
                {
                    ByteReader chunkReader = currentChunk.ChunkReader;

                    width                        = chunkReader.ReadInt32BE();
                    height                       = chunkReader.ReadInt32BE();
                    fileHeader.Size              = new Vector2(width, height);
                    fileHeader.BitDepth          = chunkReader.ReadByte();
                    fileHeader.ColorType         = chunkReader.ReadByte();
                    fileHeader.CompressionMethod = chunkReader.ReadByte();
                    fileHeader.FilterMethod      = chunkReader.ReadByte();
                    fileHeader.InterlaceMethod   = chunkReader.ReadByte();
                    break;
                }

                case PngChunkTypes.DATA:
                    dataStream.AddMemory(currentChunk.ChunkReader.Data);
                    break;

                case PngChunkTypes.PALETTE:
                    palette = currentChunk.ChunkReader.Data;
                    break;

                case PngChunkTypes.PALETTE_ALPHA:
                    paletteAlpha = currentChunk.ChunkReader.Data;
                    break;

                case PngChunkTypes.END:
                    endChunkReached = true;
                    break;
                }
            }

            // Decompress data.
            PerfProfiler.ProfilerEventStart("PNG Decompression", "Loading");
            byte[] data = ZlibStreamUtility.Decompress(dataStream);
            PerfProfiler.ProfilerEventEnd("PNG Decompression", "Loading");
            if (data == null)
            {
                return(null);
            }

            var         channelsPerColor    = 0;
            int         bytesPerPixelOutput = 4;
            ColorReader reader;

            fileHeader.PixelFormat = PixelFormat.Bgra; // Default.
            switch (fileHeader.ColorType)
            {
            case 0:
                // Grayscale - No Alpha
                channelsPerColor    = 1;
                bytesPerPixelOutput = 1;
                reader = Grayscale;
                fileHeader.PixelFormat = PixelFormat.Red;
                break;

            case 2:
                // RGB
                channelsPerColor = 3;
                reader           = Rgb;
                break;

            case 3:
                // Palette
                channelsPerColor = 1;
                reader           = (rowPixels, row, imageDest, destRow) => { Palette(palette.Span, paletteAlpha.Span, rowPixels, row, imageDest, destRow); };
                break;

            case 4:
                // Grayscale - Alpha
                channelsPerColor = 2;
                reader           = GrayscaleAlpha;
                break;

            case 6:
                // RGBA
                channelsPerColor       = 4;
                reader                 = Rgba;
                fileHeader.PixelFormat = PixelFormat.Rgba;
                break;

            default:
                reader = null;
                break;
            }

            // Unknown color mode.
            if (reader == null)
            {
                Engine.Log.Warning($"Unsupported color type - {fileHeader.ColorType}", MessageSource.ImagePng);
                return(new byte[width * height * bytesPerPixelOutput]);
            }

            // Calculate the bytes per pixel.
            var bytesPerPixel = 1;

            if (fileHeader.BitDepth == 0 || fileHeader.BitDepth == 3)
            {
                Engine.Log.Warning("Invalid bit depth.", MessageSource.ImagePng);
                return(null);
            }
            if (fileHeader.BitDepth != 8)
            {
                Engine.Log.Warning("Loading PNGs with a bit depth different than 8 will be deprecated in future versions.", MessageSource.ImagePng);
            }
            if (fileHeader.BitDepth >= 8)
            {
                bytesPerPixel = channelsPerColor * fileHeader.BitDepth / 8;
            }

            // Check interlacing.
            if (fileHeader.InterlaceMethod == 1)
            {
                Engine.Log.Warning("Loading interlaced PNGs will be deprecated in future versions. Convert your images!", MessageSource.ImagePng);
                return(ParseInterlaced(data, fileHeader, bytesPerPixel, channelsPerColor, reader));
            }

            int scanlineLength = GetScanlineLength(fileHeader, channelsPerColor) + 1;
            int scanLineCount  = data.Length / scanlineLength;

            return(Parse(scanlineLength, scanLineCount, data, bytesPerPixel, fileHeader, reader, bytesPerPixelOutput));
        }
Example #2
0
        protected override void CreateInternal(ReadOnlyMemory <byte> data)
        {
            var str = new ReadOnlyLinkedMemoryStream();

            str.AddMemory(data);

            _assContext ??= new AssimpContext();
            Scene scene = _assContext.ImportFileFromStream(str,
                                                           PostProcessSteps.Triangulate |
                                                           PostProcessSteps.FlipUVs |
                                                           PostProcessSteps.OptimizeGraph |
                                                           PostProcessSteps.OptimizeMeshes);

            var embeddedTextures = new List <Texture>();

            for (var i = 0; i < scene.TextureCount; i++)
            {
                EmbeddedTexture assTexture      = scene.Textures[i];
                var             embeddedTexture = new TextureAsset();
                embeddedTexture.Create(assTexture.CompressedData);
                embeddedTextures.Add(embeddedTexture.Texture);
            }

            _materials = new List <MeshMaterial>();
            for (var i = 0; i < scene.MaterialCount; i++)
            {
                Material material  = scene.Materials[i];
                Color4D  diffColor = material.ColorDiffuse;

                bool embeddedTexture = material.HasTextureDiffuse && embeddedTextures.Count > material.TextureDiffuse.TextureIndex;
                var  emotionMaterial = new MeshMaterial
                {
                    Name               = material.Name,
                    DiffuseColor       = new Color(new Vector4(diffColor.R, diffColor.G, diffColor.B, diffColor.A)),
                    DiffuseTextureName = embeddedTexture ? $"EmbeddedTexture{material.TextureDiffuse.TextureIndex}" : null,
                    DiffuseTexture     = embeddedTexture ? embeddedTextures[material.TextureDiffuse.TextureIndex] : null
                };

                _materials.Add(emotionMaterial);
            }

            _animations = new List <SkeletalAnimation>();
            ProcessAnimations(scene);

            _meshes = new List <Mesh>();
            Node rootNode = scene.RootNode;
            SkeletonAnimRigNode animRigRoot = ProcessNode(scene, rootNode);

            animRigRoot.LocalTransform *= Matrix4x4.CreateRotationX(-90 * Maths.DEG2_RAD); // Convert to right handed Z is up.

            Entity = new MeshEntity
            {
                Name         = Name,
                Meshes       = _meshes.ToArray(),
                Animations   = _animations.ToArray(),
                AnimationRig = animRigRoot
            };

            object scaleData = scene.RootNode.Metadata.GetValueOrDefault("UnitScaleFactor").Data;
            var    scaleF    = 1f;

            if (scaleData is float f)
            {
                scaleF = f;
            }
            Entity.Scale = scaleF;
        }