Beispiel #1
0
        public static void RegenerateMipmapsFromTexture2D(NutTexture tex)
        {
            if (!TextureFormatTools.IsCompressed(tex.pixelInternalFormat))
            {
                return;
            }

            //Rendering.OpenTKSharedResources.dummyResourceWindow.MakeCurrent();

            // Create an OpenGL texture with generated mipmaps.
            Texture2D texture2D = new Texture2D();

            texture2D.LoadImageData(tex.Width, tex.Height, tex.surfaces[0].mipmaps[0], (InternalFormat)tex.pixelInternalFormat);

            texture2D.Bind();

            for (int i = 0; i < tex.surfaces[0].mipmaps.Count; i++)
            {
                // Get the image size for the current mip level of the bound texture.
                int imageSize;
                GL.GetTexLevelParameter(TextureTarget.Texture2D, i,
                                        GetTextureParameter.TextureCompressedImageSize, out imageSize);

                byte[] mipLevelData = new byte[imageSize];

                // Replace the Nut texture with the OpenGL texture's data.
                GL.GetCompressedTexImage(TextureTarget.Texture2D, i, mipLevelData);
                tex.surfaces[0].mipmaps[i] = mipLevelData;
            }
        }
Beispiel #2
0
        /*
         * public static bool texIdUsed(int texId)
         * {
         *  foreach (var nut in Runtime.TextureContainers)
         *      foreach (NutTexture tex in nut.Nodes)
         *          if (tex.HashId == texId)
         *              return true;
         *  return false;
         * }
         *
         * public void ChangeTextureIds(int newTexId)
         * {
         *  // Check if tex ID fixing would cause any naming conflicts.
         *  if (TexIdDuplicate4thByte())
         *  {
         *      MessageBox.Show("The first six digits should be the same for all textures to prevent duplicate IDs after changing the Tex ID.",
         *          "Duplicate Texture ID");
         *      return;
         *  }
         *
         *  foreach (NutTexture tex in Textures)
         *  {
         *      Texture originalTexture = glTexByHashId[tex.HashId];
         *      glTexByHashId.Remove(tex.HashId);
         *
         *      // Only change the first 3 bytes.
         *      tex.HashId = tex.HashId & 0xFF;
         *      int first3Bytes = (int)(newTexId & 0xFFFFFF00);
         *      tex.HashId = tex.HashId | first3Bytes;
         *
         *      glTexByHashId.Add(tex.HashId, originalTexture);
         *  }
         * }
         *
         * public bool TexIdDuplicate4thByte()
         * {
         *  // Check for duplicates.
         *  List<byte> previous4thBytes = new List<byte>();
         *  foreach (NutTexture tex in Textures)
         *  {
         *      byte fourthByte = (byte)(tex.HashId & 0xFF);
         *      if (!(previous4thBytes.Contains(fourthByte)))
         *          previous4thBytes.Add(fourthByte);
         *      else
         *          return true;
         *
         *  }
         *
         *  return false;
         * }
         */
        public static Texture2D CreateTexture2D(NutTexture nutTexture, int surfaceIndex = 0)
        {
            bool compressedFormatWithMipMaps = TextureFormatTools.IsCompressed(nutTexture.pixelInternalFormat);

            List <byte[]> mipmaps = nutTexture.surfaces[surfaceIndex].mipmaps;

            if (compressedFormatWithMipMaps)
            {
                // HACK: Skip loading mipmaps for non square textures for now.
                // The existing mipmaps don't display properly for some reason.
                if (nutTexture.surfaces[0].mipmaps.Count > 1 && nutTexture.isDds && (nutTexture.Width == nutTexture.Height))
                {
                    // Reading mipmaps past the first level is only supported for DDS currently.
                    Texture2D texture = new Texture2D();
                    texture.LoadImageData(nutTexture.Width, nutTexture.Height, nutTexture.surfaces[surfaceIndex].mipmaps,
                                          (InternalFormat)nutTexture.pixelInternalFormat);
                    return(texture);
                }
                else
                {
                    // Only load the first level and generate the rest.
                    Texture2D texture = new Texture2D();
                    texture.LoadImageData(nutTexture.Width, nutTexture.Height, mipmaps[0], (InternalFormat)nutTexture.pixelInternalFormat);
                    return(texture);
                }
            }
            else
            {
                // Uncompressed.
                Texture2D texture = new Texture2D();
                texture.LoadImageData(nutTexture.Width, nutTexture.Height, mipmaps[0],
                                      new TextureFormatUncompressed(nutTexture.pixelInternalFormat, nutTexture.pixelFormat, nutTexture.pixelType));
                return(texture);
            }
        }
Beispiel #3
0
        public bool getTextureByID(int hash, out NutTexture suc)
        {
            suc = null;
            foreach (NutTexture t in Textures)
            {
                if (t.HashId == hash)
                {
                    suc = t;
                    return(true);
                }
            }

            return(false);
        }
Beispiel #4
0
        public void ConvertToDdsNut(bool regenerateMipMaps = true)
        {
            for (int i = 0; i < Textures.Count; i++)
            {
                NutTexture originalTexture = (NutTexture)Textures[i];

                // Reading/writing mipmaps is only supported for DDS textures,
                // so we will need to convert all the textures.
                DDS        dds        = new DDS(originalTexture);
                NutTexture ddsTexture = dds.ToNutTexture();
                ddsTexture.HashId = originalTexture.HashId;

                if (regenerateMipMaps)
                {
                    RegenerateMipmapsFromTexture2D(ddsTexture);
                }

                Textures[i] = ddsTexture;
            }
        }
Beispiel #5
0
 public static TextureCubeMap CreateTextureCubeMap(NutTexture t)
 {
     if (TextureFormatTools.IsCompressed(t.pixelInternalFormat))
     {
         // Compressed cubemap with mipmaps.
         TextureCubeMap texture = new TextureCubeMap();
         texture.LoadImageData(t.Width, (InternalFormat)t.pixelInternalFormat,
                               t.surfaces[0].mipmaps, t.surfaces[1].mipmaps, t.surfaces[2].mipmaps,
                               t.surfaces[3].mipmaps, t.surfaces[4].mipmaps, t.surfaces[5].mipmaps);
         return(texture);
     }
     else
     {
         // Uncompressed cube map with no mipmaps.
         TextureCubeMap texture = new TextureCubeMap();
         texture.LoadImageData(t.Width, new TextureFormatUncompressed(t.pixelInternalFormat, t.pixelFormat, t.pixelType),
                               t.surfaces[0].mipmaps[0], t.surfaces[1].mipmaps[0], t.surfaces[2].mipmaps[0],
                               t.surfaces[3].mipmaps[0], t.surfaces[4].mipmaps[0], t.surfaces[5].mipmaps[0]);
         return(texture);
     }
 }
Beispiel #6
0
        public NutTexture ToNutTexture()
        {
            NutTexture tex = new NutTexture();

            tex.isDds  = true;
            tex.HashId = 0x48415348;
            tex.Height = (int)header.height;
            tex.Width  = (int)header.width;
            byte surfaceCount = 1;
            bool isCubemap    = (header.caps2 & (uint)DDSCAPS2.CUBEMAP) == (uint)DDSCAPS2.CUBEMAP;

            if (isCubemap)
            {
                if ((header.caps2 & (uint)DDSCAPS2.CUBEMAP_ALLFACES) == (uint)DDSCAPS2.CUBEMAP_ALLFACES)
                {
                    surfaceCount = 6;
                }
                else
                {
                    throw new NotImplementedException($"Unsupported cubemap face amount for texture. Six faces are required.");
                }
            }

            bool isBlock = true;

            switch (header.ddspf.fourCC)
            {
            case 0x00000000:     //RGBA
                isBlock = false;
                tex.pixelInternalFormat = PixelInternalFormat.Rgba;
                tex.pixelFormat         = OpenTK.Graphics.OpenGL.PixelFormat.Rgba;
                break;

            case 0x31545844:     //DXT1
                tex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
                break;

            case 0x33545844:     //DXT3
                tex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
                break;

            case 0x35545844:     //DXT5
                tex.pixelInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
                break;

            case 0x31495441:     //ATI1
            case 0x55344342:     //BC4U
                tex.pixelInternalFormat = PixelInternalFormat.CompressedRedRgtc1;
                break;

            case 0x32495441:     //ATI2
            case 0x55354342:     //BC5U
                tex.pixelInternalFormat = PixelInternalFormat.CompressedRgRgtc2;
                break;

            default:
                MessageBox.Show("Unsupported DDS format - 0x" + header.ddspf.fourCC.ToString("x"));
                break;
            }

            uint formatSize = getFormatSize(header.ddspf.fourCC);

            FileData d = new FileData(bdata);

            if (header.mipmapCount == 0)
            {
                header.mipmapCount = 1;
            }

            uint off = 0;

            for (byte i = 0; i < surfaceCount; ++i)
            {
                TextureSurface surface = new TextureSurface();
                uint           w = header.width, h = header.height;
                for (int j = 0; j < header.mipmapCount; ++j)
                {
                    //If texture is DXT5 and isn't square, limit the mipmaps to an amount such that width and height are each always >= 4
                    if (tex.pixelInternalFormat == PixelInternalFormat.CompressedRgbaS3tcDxt5Ext && tex.Width != tex.Height && (w < 4 || h < 4))
                    {
                        break;
                    }

                    uint s = (w * h); //Total pixels
                    if (isBlock)
                    {
                        s = (uint)(s * ((float)formatSize / 0x10)); //Bytes per 16 pixels
                        if (s < formatSize)                         //Make sure it's at least one block
                        {
                            s = formatSize;
                        }
                    }
                    else
                    {
                        s = (uint)(s * (formatSize)); //Bytes per pixel
                    }

                    w /= 2;
                    h /= 2;
                    surface.mipmaps.Add(d.getSection((int)off, (int)s));
                    off += s;
                }
                tex.surfaces.Add(surface);
            }

            return(tex);
        }
Beispiel #7
0
        public void FromNutTexture(NutTexture tex)
        {
            header                   = new Header();
            header.flags             = (uint)(DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT | DDSD.MIPMAPCOUNT | DDSD.LINEARSIZE);
            header.width             = (uint)tex.Width;
            header.height            = (uint)tex.Height;
            header.pitchOrLinearSize = (uint)tex.ImageSize;
            header.mipmapCount       = (uint)tex.surfaces[0].mipmaps.Count;
            header.caps2             = tex.DdsCaps2;
            bool isCubemap = (header.caps2 & (uint)DDSCAPS2.CUBEMAP) == (uint)DDSCAPS2.CUBEMAP;

            header.caps = (uint)DDSCAPS.TEXTURE;
            if (header.mipmapCount > 1)
            {
                header.caps |= (uint)(DDSCAPS.COMPLEX | DDSCAPS.MIPMAP);
            }
            if (isCubemap)
            {
                header.caps |= (uint)DDSCAPS.COMPLEX;
            }

            switch (tex.pixelInternalFormat)
            {
            case PixelInternalFormat.CompressedRgbaS3tcDxt1Ext:
                header.ddspf.fourCC = 0x31545844;
                header.ddspf.flags  = (uint)DDPF.FOURCC;
                break;

            case PixelInternalFormat.CompressedRgbaS3tcDxt3Ext:
                header.ddspf.fourCC = 0x33545844;
                header.ddspf.flags  = (uint)DDPF.FOURCC;
                break;

            case PixelInternalFormat.CompressedRgbaS3tcDxt5Ext:
                header.ddspf.fourCC = 0x35545844;
                header.ddspf.flags  = (uint)DDPF.FOURCC;
                break;

            case PixelInternalFormat.CompressedRedRgtc1:
                header.ddspf.fourCC = 0x31495441;
                header.ddspf.flags  = (uint)DDPF.FOURCC;
                break;

            case PixelInternalFormat.CompressedRgRgtc2:
                header.ddspf.fourCC = 0x32495441;
                header.ddspf.flags  = (uint)DDPF.FOURCC;
                break;

            case PixelInternalFormat.Rgba:
                header.ddspf.fourCC = 0x00000000;
                if (tex.pixelFormat == OpenTK.Graphics.OpenGL.PixelFormat.Rgba)
                {
                    header.ddspf.flags       = (uint)(DDPF.RGB | DDPF.ALPHAPIXELS);
                    header.ddspf.RGBBitCount = 0x8 * 4;
                    header.ddspf.RBitMask    = 0x000000FF;
                    header.ddspf.GBitMask    = 0x0000FF00;
                    header.ddspf.BBitMask    = 0x00FF0000;
                    header.ddspf.ABitMask    = 0xFF000000;
                }
                break;

            default:
                throw new NotImplementedException($"Unknown pixel format 0x{tex.pixelInternalFormat:X}");
            }

            List <byte> d = new List <byte>();

            foreach (byte[] b in tex.GetAllMipmaps())
            {
                d.AddRange(b);
            }
            bdata = d.ToArray();
        }
Beispiel #8
0
 public DDS(NutTexture tex)
 {
     FromNutTexture(tex);
 }
Beispiel #9
0
        public void ReadNTWU(FileData d, bool skipTextures = true)
        {
            d.seek(0x6);

            ushort count = d.readUShort();

            d.skip(0x8);
            int headerPtr = 0x10;

            for (ushort i = 0; i < count; ++i)
            {
                d.seek(headerPtr);

                NutTexture tex = new NutTexture();
                tex.pixelInternalFormat = PixelInternalFormat.Rgba32ui;

                int totalSize = d.readInt();
                d.skip(4);
                int dataSize   = d.readInt();
                int headerSize = d.readUShort();
                d.skip(2);

                d.skip(1);
                byte mipmapCount = d.readByte();
                d.skip(1);
                tex.setPixelFormatFromNutFormat(d.readByte());
                tex.Width  = d.readUShort();
                tex.Height = d.readUShort();
                d.readInt(); //Always 1?
                uint caps2 = d.readUInt();

                bool isCubemap    = false;
                byte surfaceCount = 1;
                if ((caps2 & (uint)DDS.DDSCAPS2.CUBEMAP) == (uint)DDS.DDSCAPS2.CUBEMAP)
                {
                    //Only supporting all six faces
                    if ((caps2 & (uint)DDS.DDSCAPS2.CUBEMAP_ALLFACES) == (uint)DDS.DDSCAPS2.CUBEMAP_ALLFACES)
                    {
                        isCubemap    = true;
                        surfaceCount = 6;
                    }
                    else
                    {
                        throw new NotImplementedException($"Unsupported cubemap face amount for texture {i} with hash 0x{tex.HashId:X}. Six faces are required.");
                    }
                }

                int dataOffset      = d.readInt() + headerPtr;
                int mipDataOffset   = d.readInt() + headerPtr;
                int gtxHeaderOffset = d.readInt() + headerPtr;
                d.readInt();

                int cmapSize1 = 0;
                int cmapSize2 = 0;
                if (isCubemap)
                {
                    cmapSize1 = d.readInt();
                    cmapSize2 = d.readInt();
                    d.skip(8);
                }

                int imageSize = 0; //Total size of first mipmap of every surface
                int mipSize   = 0; //Total size of mipmaps other than the first of every surface
                if (mipmapCount == 1)
                {
                    if (isCubemap)
                    {
                        imageSize = cmapSize1;
                    }
                    else
                    {
                        imageSize = dataSize;
                    }
                }
                else
                {
                    imageSize = d.readInt();
                    mipSize   = d.readInt();
                    d.skip((mipmapCount - 2) * 4);
                    d.align(0x10);
                }

                d.skip(0x10); //eXt data - always the same

                d.skip(4);    //GIDX
                d.readInt();  //Always 0x10
                tex.HashOffset = d.pos();
                tex.HashId     = d.readInt();

                if (!skipTextures)
                {
                    d.skip(4); // padding align 8

                    d.seek(gtxHeaderOffset);
                    GTX.GX2Surface gtxHeader = new GTX.GX2Surface();

                    gtxHeader.dim       = d.readInt();
                    gtxHeader.width     = d.readInt();
                    gtxHeader.height    = d.readInt();
                    gtxHeader.depth     = d.readInt();
                    gtxHeader.numMips   = d.readInt();
                    gtxHeader.format    = d.readInt();
                    gtxHeader.aa        = d.readInt();
                    gtxHeader.use       = d.readInt();
                    gtxHeader.imageSize = d.readInt();
                    gtxHeader.imagePtr  = d.readInt();
                    gtxHeader.mipSize   = d.readInt();
                    gtxHeader.mipPtr    = d.readInt();
                    gtxHeader.tileMode  = d.readInt();
                    gtxHeader.swizzle   = d.readInt();
                    gtxHeader.alignment = d.readInt();
                    gtxHeader.pitch     = d.readInt();

                    //mipOffsets[0] is not in this list and is simply the start of the data (dataOffset)
                    //mipOffsets[1] is relative to the start of the data (dataOffset + mipOffsets[1])
                    //Other mipOffsets are relative to mipOffset[1] (dataOffset + mipOffsets[1] + mipOffsets[i])
                    int[] mipOffsets = new int[mipmapCount];
                    mipOffsets[0] = 0;
                    for (byte mipLevel = 1; mipLevel < mipmapCount; ++mipLevel)
                    {
                        mipOffsets[mipLevel] = 0;
                        mipOffsets[mipLevel] = mipOffsets[1] + d.readInt();
                    }

                    for (byte surfaceLevel = 0; surfaceLevel < surfaceCount; ++surfaceLevel)
                    {
                        tex.surfaces.Add(new TextureSurface());
                    }

                    int w = tex.Width, h = tex.Height;
                    for (byte mipLevel = 0; mipLevel < mipmapCount; ++mipLevel)
                    {
                        int p = gtxHeader.pitch / (gtxHeader.width / w);

                        int size;
                        if (mipmapCount == 1)
                        {
                            size = imageSize;
                        }
                        else if (mipLevel + 1 == mipmapCount)
                        {
                            size = (mipSize + mipOffsets[1]) - mipOffsets[mipLevel];
                        }
                        else
                        {
                            size = mipOffsets[mipLevel + 1] - mipOffsets[mipLevel];
                        }

                        size /= surfaceCount;

                        for (byte surfaceLevel = 0; surfaceLevel < surfaceCount; ++surfaceLevel)
                        {
                            gtxHeader.data = d.getSection(dataOffset + mipOffsets[mipLevel] + (size * surfaceLevel), size);

                            //Real size
                            //Leave the below line commented for now because it breaks RGBA textures
                            //size = ((w + 3) >> 2) * ((h + 3) >> 2) * (GTX.getBPP(gtxHeader.format) / 8);
                            if (size < (GTX.getBPP(gtxHeader.format) / 8))
                            {
                                size = (GTX.getBPP(gtxHeader.format) / 8);
                            }

                            byte[] deswiz = GTX.swizzleBC(
                                gtxHeader.data,
                                w,
                                h,
                                gtxHeader.format,
                                gtxHeader.tileMode,
                                p,
                                gtxHeader.swizzle
                                );
                            tex.surfaces[surfaceLevel].mipmaps.Add(new FileData(deswiz).getSection(0, size));
                        }

                        w /= 2;
                        h /= 2;

                        if (w < 1)
                        {
                            w = 1;
                        }
                        if (h < 1)
                        {
                            h = 1;
                        }
                    }
                }

                headerPtr += headerSize;

                Textures.Add(tex);
            }
        }
Beispiel #10
0
        public void ReadNTP3(FileData d, bool skipTextures = true)
        {
            d.seek(0x6);

            ushort count = d.readUShort();

            d.skip(0x8);
            int headerPtr = 0x10;

            for (ushort i = 0; i < count; ++i)
            {
                d.seek(headerPtr);

                NutTexture tex = new NutTexture();
                tex.isDds = true;
                tex.pixelInternalFormat = PixelInternalFormat.Rgba32ui;

                int totalSize = d.readInt();
                d.skip(4);
                int dataSize   = d.readInt();
                int headerSize = d.readUShort();
                d.skip(2);

                //It might seem that mipmapCount and pixelFormat would be shorts, but they're bytes because they stay in the same place regardless of endianness
                d.skip(1);
                byte mipmapCount = d.readByte();
                d.skip(1);
                tex.setPixelFormatFromNutFormat(d.readByte());
                tex.Width  = d.readUShort();
                tex.Height = d.readUShort();
                d.skip(4); //0 in dds nuts (like NTP3) and 1 in gtx nuts; texture type?
                uint caps2 = d.readUInt();

                bool isCubemap    = false;
                byte surfaceCount = 1;
                if ((caps2 & (uint)DDS.DDSCAPS2.CUBEMAP) == (uint)DDS.DDSCAPS2.CUBEMAP)
                {
                    //Only supporting all six faces
                    if ((caps2 & (uint)DDS.DDSCAPS2.CUBEMAP_ALLFACES) == (uint)DDS.DDSCAPS2.CUBEMAP_ALLFACES)
                    {
                        isCubemap    = true;
                        surfaceCount = 6;
                    }
                    else
                    {
                        throw new NotImplementedException($"Unsupported cubemap face amount for texture {i} with hash 0x{tex.HashId:X}. Six faces are required.");
                    }
                }

                int dataOffset = 0;
                if (Version < 0x0200)
                {
                    dataOffset = headerPtr + headerSize;
                    d.readInt();
                }
                else if (Version >= 0x0200)
                {
                    dataOffset = d.readInt() + headerPtr;
                }
                d.readInt();
                d.readInt();
                d.readInt();

                //The size of a single cubemap face (discounting mipmaps). I don't know why it is repeated. If mipmaps are present, this is also specified in the mipSize section anyway.
                int cmapSize1 = 0;
                int cmapSize2 = 0;
                if (isCubemap)
                {
                    cmapSize1 = d.readInt();
                    cmapSize2 = d.readInt();
                    d.skip(8);
                }

                int[] mipSizes = new int[mipmapCount];
                if (mipmapCount == 1)
                {
                    if (isCubemap)
                    {
                        mipSizes[0] = cmapSize1;
                    }
                    else
                    {
                        mipSizes[0] = dataSize;
                    }
                }
                else
                {
                    for (byte mipLevel = 0; mipLevel < mipmapCount; ++mipLevel)
                    {
                        mipSizes[mipLevel] = d.readInt();
                    }
                    d.align(0x10);
                }

                d.skip(0x10); //eXt data - always the same

                d.skip(4);    //GIDX
                d.readInt();  //Always 0x10
                tex.HashOffset = d.pos();
                tex.HashId     = d.readInt();
                d.skip(4); // padding align 8

                if (!skipTextures)
                {
                    for (byte surfaceLevel = 0; surfaceLevel < surfaceCount; ++surfaceLevel)
                    {
                        TextureSurface surface = new TextureSurface();
                        for (byte mipLevel = 0; mipLevel < mipmapCount; ++mipLevel)
                        {
                            byte[] texArray = d.getSection(dataOffset, mipSizes[mipLevel]);
                            surface.mipmaps.Add(texArray);
                            dataOffset += mipSizes[mipLevel];
                        }
                        tex.surfaces.Add(surface);
                    }

                    if (tex.getNutFormat() == 14 || tex.getNutFormat() == 17)
                    {
                        tex.SwapChannelOrderUp();
                    }
                }

                if (Version < 0x0200)
                {
                    headerPtr += totalSize;
                }
                else if (Version >= 0x0200)
                {
                    headerPtr += headerSize;
                }

                Textures.Add(tex);
            }
        }