public static rTexture FromDDSImage(DDSImage image, string fileName, DatumIndex datum, ResourceType fileType, bool isBigEndian) { // Create a new texture we can populate with info. rTexture texture = new rTexture(fileName, datum, fileType, isBigEndian); // Fill out the header fields. texture.header.Magic = rTextureHeader.kMagic; texture.header.Version = rTextureHeader.kVersion; texture.header.Width = image.Width; texture.header.Height = image.Height; texture.header.MipMapCount = (byte)image.MipMapCount; texture.header.Depth = image.Depth; // Check various flags to determine what type of texture this is. if (image.Flags.HasFlag(DDSD_FLAGS.DDSD_DEPTH) == true) { // Set the depthmap texture type. texture.header.TextureType = TextureType.Type_DepthMap; // Copy the DDS header from the image. texture.depthMapHeader = image.header; } else if (image.Capabilities2.HasFlag(DDSCAPS2.DDSCAPS2_CUBEMAP_ALLFACES) == true) { texture.header.TextureType = TextureType.Type_CubeMap; } else { texture.header.TextureType = TextureType.Type_2D; } // Check the image's fourcc code to determine the texture format. if (image.Format == 0) { // Check the bit count and color masks to determine the texture format. if (image.header.ddspf.dwFlags.HasFlag(DDPF.DDPF_BUMPDUDV) == true || (image.BitCount == 16 && image.RBitMask == 0xFF && image.GBitMask == 0xFF00)) { texture.header.Format = TextureFormat.Format_R8G8_SNORM; } else if (image.BitCount == 32) { texture.header.Format = TextureFormat.Format_B8G8R8A8_UNORM; } // TODO: Do we need to re-encode? } else { // Use the fourcc code to determine the texture format. texture.header.Format = DDSImage.TextureFormatFromFourCC(image.Format); } // Set the number of faces this texture has based on the texture type. if (texture.header.TextureType == TextureType.Type_CubeMap) { // Cubemap has 6 faces each with n mip map levels. texture.FaceCount = 6; } else { // All other texture types have 1 face with n mip maps. texture.FaceCount = 1; } // Calculate the total size of the pixel buffer. int pixelBufferSize = CalculatePixelBufferSize(texture.header.Width, texture.header.Height, texture.FaceCount, texture.header.MipMapCount, texture.header.Format, texture.header.TextureType); // Check if we need to pad the pixel buffer from the DDS image. byte[] pixelBuffer = image.PixelBuffer; if (pixelBufferSize > image.PixelBuffer.Length) { // Allocate a new array that is the correct size. pixelBuffer = new byte[pixelBufferSize]; // Copy in the pixel buffer from the DDS image and pad the remaining bytes. Array.Copy(image.PixelBuffer, pixelBuffer, image.PixelBuffer.Length); for (int i = image.PixelBuffer.Length; i < pixelBufferSize; i++) { pixelBuffer[i] = 0xCD; } } // Create the datastream using the pixel buffer we prepared. texture.PixelDataStream = DataStream.Create(pixelBuffer, true, false); // Make sure we are at the start of the pixel data stream. texture.PixelDataStream.Seek(0, SeekOrigin.Begin); // Allocate the sub resources array and setup for every face the texture has. texture.SubResources = new DataBox[texture.FaceCount * texture.header.MipMapCount]; for (int i = 0; i < texture.FaceCount; i++) { // Loop for the number of mip maps and read each one. for (int x = 0; x < texture.header.MipMapCount; x++) { int bytesPerRow = 0; int numberOfBlocks = 0; // Calculate the pitch values for the current mip level. int mipHeight = texture.header.TextureType == TextureType.Type_CubeMap ? texture.header.Width : texture.header.Height; CalculateMipMapPitch(texture.header.Width, mipHeight, x, texture.header.Format, out bytesPerRow, out numberOfBlocks); // Setup the resource description for this mip map. texture.SubResources[(i * texture.header.MipMapCount) + x] = new DataBox(texture.PixelDataStream.PositionPointer, bytesPerRow, bytesPerRow * numberOfBlocks); texture.PixelDataStream.Seek(bytesPerRow * numberOfBlocks, SeekOrigin.Current); } } // Return the texture. return(texture); }
public static DDSImage FromGameTexture(rTexture texture) { // Create a new DDSImage to populate with info. DDSImage image = new DDSImage(); image.header = new DDS_HEADER(); image.header.dwMagic = DDS_HEADER.kMagic; image.header.dwSize = DDS_HEADER.kSizeOf; image.header.dwFlags = DDSD_FLAGS.DDSD_CAPS | DDSD_FLAGS.DDSD_HEIGHT | DDSD_FLAGS.DDSD_WIDTH | DDSD_FLAGS.DDSD_PIXELFORMAT;// | DDSD_FLAGS.DDSD_MIPMAPCOUNT; image.header.dwHeight = texture.Height; image.header.dwWidth = texture.Width; rTexture.CalculateMipMapPitch(texture.Width, texture.Height, 0, texture.Format, out image.header.dwPitchOrLinearSize, out int blocks); image.header.dwDepth = texture.Depth; image.header.dwMipMapCount = texture.MipMapCount; image.header.ddspf = new DDS_PIXELFORMAT(); image.header.ddspf.dwSize = DDS_PIXELFORMAT.kSizeOf; image.header.ddspf.dwFourCC = FourCCFromTextureFormat(texture.Format); image.header.dwCaps = DDSCAPS.DDSCAPS_TEXTURE; // If there are more than 1 mip maps or this is a cube map set the mip map flags. if (texture.MipMapCount > 0 || texture.Type == TextureType.Type_CubeMap) { // Set additioanl mip map flags. image.header.dwFlags |= DDSD_FLAGS.DDSD_MIPMAPCOUNT; image.header.dwCaps |= DDSCAPS.DDSCAPS_MIPMAP | DDSCAPS.DDSCAPS_COMPLEX; // Cubemap only flags. if (texture.Type == TextureType.Type_CubeMap) { image.header.dwCaps2 |= DDSCAPS2.DDSCAPS2_CUBEMAP_ALLFACES; } } // Set additional fields depending on if the texture is a compressed format or not. if (texture.Format == TextureFormat.Format_DXT1 || texture.Format == TextureFormat.Format_DXT2 || texture.Format == TextureFormat.Format_DXT5) { // Pitch field is linear size (pitch of 1 compressed block). image.header.dwFlags |= DDSD_FLAGS.DDSD_LINEARSIZE; // Set the fourcc format. image.header.ddspf.dwFlags |= DDPF.DDPF_FOURCC; } else { // Set RGBA bit masks based on the texture format. switch (texture.Format) { case TextureFormat.Format_B8G8R8A8_UNORM: { image.header.ddspf.dwFlags |= DDPF.DDPF_RGB; image.header.ddspf.dwRGBBitCount = 32; image.header.ddspf.dwRBitMask = 0xFF0000; image.header.ddspf.dwGBitMask = 0xFF00; image.header.ddspf.dwBBitMask = 0xFF; image.header.ddspf.dwABitMask = 0xFF000000; break; } case TextureFormat.Format_R8G8_SNORM: { image.header.ddspf.dwFlags |= DDPF.DDPF_BUMPDUDV; image.header.ddspf.dwRGBBitCount = 16; image.header.ddspf.dwRBitMask = 0xFF; image.header.ddspf.dwGBitMask = 0xFF00; break; } default: { break; } } //// Setup DX10 header with the correct format value. //image.dxt10header = new DDS_HEADER_DXT10(); //image.dxt10header.dxgiFormat = rTexture.DXGIFromTextureFormat(texture.Format); //// Set the resource dimensions based on the texture type. //if (texture.Type == TextureType.Type_2D || texture.Type == TextureType.Type_CubeMap) // image.dxt10header.resourceDimension = ResourceDimension.Texture2D; //else if (texture.Type == TextureType.Type_DepthMap) // image.dxt10header.resourceDimension = ResourceDimension.Texture3D; //// Set special cube map flags. //if (texture.Type == TextureType.Type_CubeMap) // image.dxt10header.miscFlag = ResourceMiscFlags.DDS_RESOURCE_MISC_TEXTURECUBE; //// TODO: miscFlags2 } // If the texture is a depth map add the depth flag. if (texture.Type == TextureType.Type_DepthMap) { image.header.dwFlags |= DDSD_FLAGS.DDSD_DEPTH; } // Allocate the pixel buffer. image.PixelBuffer = new byte[(int)texture.PixelDataStream.Length]; // Copy the pixel buffer from the texture. texture.PixelDataStream.Seek(0, System.IO.SeekOrigin.Begin); texture.PixelDataStream.Read(image.PixelBuffer, 0, image.PixelBuffer.Length); //// Check if we need to convert the pixel buffer for none DXT formats. //switch (texture.Format) //{ // case TextureFormat.Format_R8G8_SNORM: // { // image.PixelBuffer = DXTDecoder.DecodeR8G8(image.Width, image.Height, image.PixelBuffer, texture.IsBigEndian); // break; // } // default: break; //} // Return the DDS image. return(image); }
public static rTexture FromGameResource(byte[] buffer, string fileName, DatumIndex datum, ResourceType fileType, bool isBigEndian) { // Make sure the buffer is large enough to contain the texture header. if (buffer.Length < rTextureHeader.kSizeOf) { return(null); } // Create a new texture object to populate with data. rTexture texture = new rTexture(fileName, datum, fileType, isBigEndian); // Create a new memory stream and binary reader for the buffer. MemoryStream ms = new MemoryStream(buffer); EndianReader reader = new EndianReader(isBigEndian == true ? Endianness.Big : Endianness.Little, ms); // Parse the header. texture.header = new rTextureHeader(); texture.header.Magic = reader.ReadInt32(); texture.header.Version = reader.ReadByte(); texture.header.TextureType = (TextureType)reader.ReadByte(); texture.header.Flags = (TextureFlags)reader.ReadByte(); texture.header.MipMapCount = reader.ReadByte(); texture.header.Width = reader.ReadInt32(); texture.header.Height = reader.ReadInt32(); texture.header.Depth = reader.ReadInt32(); int fourcc = reader.ReadInt32(); texture.header.Format = TextureFormatFromFourCC(fourcc); texture.Swizzled = IsXboxFormat(fourcc); texture.XboxFormat = IsXboxFormat(fourcc); // Verify the magic is correct. if (texture.header.Magic != rTextureHeader.kMagic) { // Header has invalid magic. return(null); } // Check the version is supported. if (texture.header.Version != rTextureHeader.kVersion) { // Texture is an unsupported version. return(null); } // Make sure the texture format is supported. if (texture.header.Format == TextureFormat.Format_Unsupported) { // Texture is unsupported format. return(null); } // Check if we need to read the background color. if (texture.header.Flags.HasFlag(TextureFlags.HasD3DClearColor) == true) { // Read the RGBA background color. texture.BackgroundColor[0] = reader.ReadSingle(); texture.BackgroundColor[1] = reader.ReadSingle(); texture.BackgroundColor[2] = reader.ReadSingle(); texture.BackgroundColor[3] = reader.ReadSingle(); } // Check for some unknown blob. if (texture.header.TextureType == TextureType.Type_CubeMap) { // Read 108 bytes. // These could be vertex coordinates for 9 vertices that make up the box the cubemap gets rendered on? texture.cubemapData = reader.ReadBytes(108); } // TODO: Properly handle tiling on xbox 360 textures. //if (texture.Swizzled == true) //{ // //pixelData = Swizzle.ConvertToLinearTexture(pixelData, texture.header.Width, texture.header.Height, texture.header.Format); // int rowPitch = ((texture.header.Width + 3) / 4);// * RowPitchFromTextureFormat(texture.header.Format); // pixelData = Swizzle.XGUntileTextureLevel((uint)texture.header.Width, (uint)texture.header.Height, 0, texture.header.Format, // Swizzle.XGTILE.XGTILE_BORDER, (uint)rowPitch, null, pixelData, null); //} // If the texture type is a raw DDS file read the DDS header now. if (texture.header.TextureType == TextureType.Type_DepthMap) { // Read the DDS image header. texture.depthMapHeader = new DDS_HEADER(); texture.depthMapHeader.dwMagic = reader.ReadInt32(); texture.depthMapHeader.dwSize = reader.ReadInt32(); texture.depthMapHeader.dwFlags = (DDSD_FLAGS)reader.ReadInt32(); texture.depthMapHeader.dwHeight = reader.ReadInt32(); texture.depthMapHeader.dwWidth = reader.ReadInt32(); texture.depthMapHeader.dwPitchOrLinearSize = reader.ReadInt32(); texture.depthMapHeader.dwDepth = reader.ReadInt32(); texture.depthMapHeader.dwMipMapCount = reader.ReadInt32(); reader.BaseStream.Position += sizeof(int) * 11; texture.depthMapHeader.ddspf = new DDS_PIXELFORMAT(); texture.depthMapHeader.ddspf.dwSize = reader.ReadInt32(); texture.depthMapHeader.ddspf.dwFlags = (DDPF)reader.ReadInt32(); texture.depthMapHeader.ddspf.dwFourCC = reader.ReadInt32(); texture.depthMapHeader.ddspf.dwRGBBitCount = reader.ReadInt32(); texture.depthMapHeader.ddspf.dwRBitMask = reader.ReadUInt32(); texture.depthMapHeader.ddspf.dwGBitMask = reader.ReadUInt32(); texture.depthMapHeader.ddspf.dwBBitMask = reader.ReadUInt32(); texture.depthMapHeader.ddspf.dwABitMask = reader.ReadUInt32(); texture.depthMapHeader.dwCaps = (DDSCAPS)reader.ReadInt32(); texture.depthMapHeader.dwCaps2 = (DDSCAPS2)reader.ReadInt32(); texture.depthMapHeader.dwCaps3 = reader.ReadInt32(); texture.depthMapHeader.dwCaps4 = reader.ReadInt32(); reader.BaseStream.Position += sizeof(int); // BUG: need to check for dx10 header. // Check the header magic and structure sizes for sanity. if (texture.depthMapHeader.dwMagic != DDS_HEADER.kMagic || texture.depthMapHeader.dwSize != DDS_HEADER.kSizeOf || texture.depthMapHeader.ddspf.dwSize != DDS_PIXELFORMAT.kSizeOf) { // DDS header is invalid. return(null); } } // Read all of the pixel data now and pin it so we can build the sub resources array. byte[] pixelData = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); texture.PixelDataStream = DataStream.Create(pixelData, true, false); // Make sure we are at the start of the pixel data stream. texture.PixelDataStream.Seek(0, SeekOrigin.Begin); // Set the number of faces this texture has based on the texture type. if (texture.header.TextureType == TextureType.Type_CubeMap) { // Cubemap has 6 faces each with n mip map levels. texture.FaceCount = 6; } else { // All other texture types have 1 face with n mip maps. texture.FaceCount = 1; } // Allocate the sub resources array and setup for every face the texture has. texture.SubResources = new DataBox[texture.FaceCount * texture.header.MipMapCount]; for (int i = 0; i < texture.FaceCount; i++) { // Loop for the number of mip maps and read each one. for (int x = 0; x < texture.header.MipMapCount; x++) { int bytesPerRow = 0; int numberOfBlocks = 0; // Calculate the pitch values for the current mip level. int mipHeight = texture.header.TextureType == TextureType.Type_CubeMap ? texture.header.Width : texture.header.Height; CalculateMipMapPitch(texture.header.Width, mipHeight, x, texture.header.Format, out bytesPerRow, out numberOfBlocks); // Setup the resource description for this mip map. texture.SubResources[(i * texture.header.MipMapCount) + x] = new DataBox(texture.PixelDataStream.PositionPointer, bytesPerRow, bytesPerRow * numberOfBlocks); texture.PixelDataStream.Seek(bytesPerRow * numberOfBlocks, SeekOrigin.Current); } } // Close the binary reader and memory stream. reader.Close(); ms.Close(); // Return the texture object. return(texture); }