/// <summary> /// Equalizes the size of the textures by scaling to the largest known texture size /// </summary> /// <param name="texMapData">The texture map data containing the texture bytes</param> /// <returns>The width and height that the textures were equalized to.</returns> private (int Width, int Height) EqualizeTextureSizes(TexMapData texMapData) { // Normal map is chosen because almost every item has a normal map, diffuse is chosen otherwise var width = 0; var height = 0; var largestSize = 0; if (texMapData.Normal != null) { width = texMapData.Normal.Width; height = texMapData.Normal.Height; largestSize = width * height; } else if (texMapData.Diffuse != null) { width = texMapData.Diffuse.Width; height = texMapData.Diffuse.Height; largestSize = width * height; } var scaleDown = false; var scale = 1; if (texMapData.Normal != null) { var size = texMapData.Normal.Width * texMapData.Normal.Height; if (size > largestSize) { largestSize = size; width = texMapData.Normal.Width; height = texMapData.Normal.Height; } } if (texMapData.Diffuse != null) { var size = texMapData.Diffuse.Width * texMapData.Diffuse.Height; if (size > largestSize) { largestSize = size; width = texMapData.Diffuse.Width; height = texMapData.Diffuse.Height; } } if (texMapData.Specular != null) { var size = texMapData.Specular.Width * texMapData.Specular.Height; if (size > largestSize) { largestSize = size; width = texMapData.Specular.Width; height = texMapData.Specular.Height; } } if (texMapData.Multi != null) { var size = texMapData.Multi.Width * texMapData.Multi.Height; if (size > largestSize) { largestSize = size; width = texMapData.Multi.Width; height = texMapData.Multi.Height; } } if (texMapData.Skin != null) { var size = texMapData.Skin.Width * texMapData.Skin.Height; if (size > largestSize) { largestSize = size; width = texMapData.Skin.Width; height = texMapData.Skin.Height; } } if (width > 4000 || height > 4000) { scale = 4; scaleDown = true; } //else if (width > 2000 || height > 2000) //{ // scale = 2; // scaleDown = true; //} width = width / scale; height = height / scale; largestSize = width * height; if (texMapData.Normal != null && largestSize > texMapData.Normal.Width * texMapData.Normal.Height || scaleDown) { var pixelSettings = new PixelReadSettings(texMapData.Normal.Width, texMapData.Normal.Height, StorageType.Char, PixelMapping.RGBA); using (var image = new MagickImage(texMapData.Normal.Data, pixelSettings)) { var size = new MagickGeometry(width, height); size.IgnoreAspectRatio = true; image.Resize(size); texMapData.Normal.Width = width; texMapData.Normal.Height = height; texMapData.Normal.Data = image.ToByteArray(MagickFormat.Rgba); } } if (texMapData.Diffuse != null && (largestSize > texMapData.Diffuse.Width * texMapData.Diffuse.Height || scaleDown)) { var pixelSettings = new PixelReadSettings(texMapData.Diffuse.Width, texMapData.Diffuse.Height, StorageType.Char, PixelMapping.RGBA); using (var image = new MagickImage(texMapData.Diffuse.Data, pixelSettings)) { image.Alpha(AlphaOption.Off); var size = new MagickGeometry(width, height); size.IgnoreAspectRatio = true; image.Resize(size); texMapData.Diffuse.Width = width; texMapData.Diffuse.Height = height; texMapData.Diffuse.Data = image.ToByteArray(MagickFormat.Rgba); } } if (texMapData.Specular != null && (largestSize > texMapData.Specular.Width * texMapData.Specular.Height || scaleDown)) { var pixelSettings = new PixelReadSettings(texMapData.Specular.Width, texMapData.Specular.Height, StorageType.Char, PixelMapping.RGBA); using (var image = new MagickImage(texMapData.Specular.Data, pixelSettings)) { var size = new MagickGeometry(width, height); size.IgnoreAspectRatio = true; image.Resize(size); texMapData.Specular.Width = width; texMapData.Specular.Height = height; texMapData.Specular.Data = image.ToByteArray(MagickFormat.Rgba); } } if (texMapData.Multi != null && (largestSize > texMapData.Multi.Width * texMapData.Multi.Height || scaleDown)) { var pixelSettings = new PixelReadSettings(texMapData.Multi.Width, texMapData.Multi.Height, StorageType.Char, PixelMapping.RGBA); using (var image = new MagickImage(texMapData.Multi.Data, pixelSettings)) { var size = new MagickGeometry(width, height); size.IgnoreAspectRatio = true; image.Resize(size); texMapData.Multi.Width = width; texMapData.Multi.Height = height; texMapData.Multi.Data = image.ToByteArray(MagickFormat.Rgba); } } if (texMapData.Skin != null && (largestSize > texMapData.Skin.Width * texMapData.Skin.Height || scaleDown)) { var pixelSettings = new PixelReadSettings(texMapData.Skin.Width, texMapData.Skin.Height, StorageType.Char, PixelMapping.RGBA); using (var image = new MagickImage(texMapData.Skin.Data, pixelSettings)) { var size = new MagickGeometry(width, height); size.IgnoreAspectRatio = true; image.Resize(size); texMapData.Skin.Width = width; texMapData.Skin.Height = height; texMapData.Skin.Data = image.ToByteArray(MagickFormat.Rgba); } } return(width, height); }
/// <summary> /// Gets the data for the texture map /// </summary> /// <returns>The texure map data</returns> private TexMapData GetTexMapData() { var tex = new Tex(_gameDirectory); var texMapData = new TexMapData(); foreach (var texTypePath in _mtrlData.TextureTypePathList) { if (texTypePath.Type != XivTexType.ColorSet) { var texData = tex.GetTexData(texTypePath); var imageData = tex.GetImageData(texData); switch (texTypePath.Type) { case XivTexType.Diffuse: texMapData.Diffuse = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData }; break; case XivTexType.Specular: texMapData.Specular = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData };; break; case XivTexType.Normal: texMapData.Normal = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData };; break; case XivTexType.Multi: texMapData.Multi = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData };; break; case XivTexType.Skin: texMapData.Skin = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData };; break; default: throw new ArgumentOutOfRangeException(); } } } foreach (var texPath in _mtrlData.TexturePathList) { if (texPath.Contains("dummy")) { texMapData.HasDummyTextures = true; break; } } if (_mtrlData.ColorSetDataSize > 0) { var colorSetData = new List <byte>(); foreach (var half in _mtrlData.ColorSetData) { var colorByte = (byte)(half * 255); if (half > 1) { colorByte = 255; } colorSetData.Add(colorByte); } texMapData.ColorSet = new TexInfo { Width = 4, Height = 16, Data = colorSetData.ToArray() };; } return(texMapData); }
/// <summary> /// Retreives the raw pixel data for each texture, collated into a class to hold them. /// </summary> /// <returns>The texure map data</returns> private static async Task <TexMapData> GetTexMapData(Tex tex, XivMtrl mtrl) { var texMapData = new TexMapData(); // Use the function that returns proper sane reuslts. var ttps = mtrl.GetTextureTypePathList(); foreach (var ttp in ttps) { if (ttp.Type != XivTexType.ColorSet) { var texData = await tex.GetTexData(ttp); var imageData = await tex.GetImageData(texData); switch (ttp.Type) { case XivTexType.Diffuse: texMapData.Diffuse = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData }; break; case XivTexType.Specular: case XivTexType.Multi: case XivTexType.Skin: texMapData.Specular = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData };; break; case XivTexType.Normal: texMapData.Normal = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData };; break; default: // Do not render textures that we do not know how to use break; } } } if (mtrl.ColorSetDataSize > 0) { var colorSetData = new List <byte>(); foreach (var half in mtrl.ColorSetData) { var colorByte = (byte)(half * 255); if (half > 1) { colorByte = 255; } colorSetData.Add(colorByte); } texMapData.ColorSet = new TexInfo { Width = 4, Height = 16, Data = colorSetData.ToArray() };; } return(texMapData); }
/// <summary> /// Equalizes the size of the textures by scaling to the largest known texture size /// </summary> /// <param name="texMapData">The texture map data containing the texture bytes</param> /// <returns>The width and height that the textures were equalized to.</returns> private async Task <(int Width, int Height)> EqualizeTextureSizes(TexMapData texMapData) { // Normal map is chosen because almost every item has a normal map, diffuse is chosen otherwise var width = 0; var height = 0; var largestSize = 0; if (texMapData.Normal != null) { width = texMapData.Normal.Width; height = texMapData.Normal.Height; largestSize = width * height; } else if (texMapData.Diffuse != null) { width = texMapData.Diffuse.Width; height = texMapData.Diffuse.Height; largestSize = width * height; } var scaleDown = false; var scale = 1; if (texMapData.Normal != null) { var size = texMapData.Normal.Width * texMapData.Normal.Height; if (size > largestSize) { largestSize = size; width = texMapData.Normal.Width; height = texMapData.Normal.Height; } } if (texMapData.Diffuse != null) { var size = texMapData.Diffuse.Width * texMapData.Diffuse.Height; if (size > largestSize) { largestSize = size; width = texMapData.Diffuse.Width; height = texMapData.Diffuse.Height; } } if (texMapData.Specular != null) { var size = texMapData.Specular.Width * texMapData.Specular.Height; if (size > largestSize) { largestSize = size; width = texMapData.Specular.Width; height = texMapData.Specular.Height; } } if (texMapData.Multi != null) { var size = texMapData.Multi.Width * texMapData.Multi.Height; if (size > largestSize) { largestSize = size; width = texMapData.Multi.Width; height = texMapData.Multi.Height; } } if (texMapData.Skin != null) { var size = texMapData.Skin.Width * texMapData.Skin.Height; if (size > largestSize) { largestSize = size; width = texMapData.Skin.Width; height = texMapData.Skin.Height; } } if (width > 4000 || height > 4000) { scale = 4; scaleDown = true; } //else if (width > 2000 || height > 2000) //{ // scale = 2; // scaleDown = true; //} width = width / scale; height = height / scale; largestSize = width * height; await Task.Run(() => { if (texMapData.Normal != null && largestSize > texMapData.Normal.Width *texMapData.Normal.Height || scaleDown) { using (var img = Image.LoadPixelData <Rgba32>(texMapData.Normal.Data, texMapData.Normal.Width, texMapData.Normal.Height)) { img.Mutate(x => x.Resize(width, height)); texMapData.Normal.Data = MemoryMarshal.AsBytes(img.GetPixelSpan()).ToArray(); } } if (texMapData.Diffuse != null && (largestSize > texMapData.Diffuse.Width *texMapData.Diffuse.Height || scaleDown)) { using (var img = Image.LoadPixelData <Rgba32>(texMapData.Diffuse.Data, texMapData.Diffuse.Width, texMapData.Diffuse.Height)) { for (int i = 0; i < img.Height; i++) { var pixelRowSpan = img.GetPixelRowSpan(i); for (int j = 0; j < img.Width; j++) { pixelRowSpan[j] = new Rgba32(pixelRowSpan[j].R, pixelRowSpan[j].G, pixelRowSpan[j].B, 255); } } img.Mutate(x => x.Resize(width, height)); texMapData.Diffuse.Data = MemoryMarshal.AsBytes(img.GetPixelSpan()).ToArray(); } } if (texMapData.Specular != null && (largestSize > texMapData.Specular.Width *texMapData.Specular.Height || scaleDown)) { using (var img = Image.LoadPixelData <Rgba32>(texMapData.Specular.Data, texMapData.Specular.Width, texMapData.Specular.Height)) { img.Mutate(x => x.Resize(width, height)); texMapData.Specular.Data = MemoryMarshal.AsBytes(img.GetPixelSpan()).ToArray(); } } if (texMapData.Multi != null && (largestSize > texMapData.Multi.Width *texMapData.Multi.Height || scaleDown)) { using (var img = Image.LoadPixelData <Rgba32>(texMapData.Multi.Data, texMapData.Multi.Width, texMapData.Multi.Height)) { img.Mutate(x => x.Resize(width, height)); texMapData.Multi.Data = MemoryMarshal.AsBytes(img.GetPixelSpan()).ToArray(); } } if (texMapData.Skin != null && (largestSize > texMapData.Skin.Width *texMapData.Skin.Height || scaleDown)) { using (var img = Image.LoadPixelData <Rgba32>(texMapData.Skin.Data, texMapData.Skin.Width, texMapData.Skin.Height)) { img.Mutate(x => x.Resize(width, height)); texMapData.Skin.Data = MemoryMarshal.AsBytes(img.GetPixelSpan()).ToArray(); } } }); return(width, height); }
/// <summary> /// Equalizes the size of the textures by scaling to the largest known texture size /// </summary> /// <param name="texMapData">The texture map data containing the texture bytes</param> /// <returns>The width and height that the textures were equalized to.</returns> private static async Task <(int Width, int Height)> EqualizeTextureSizes(TexMapData texMapData) { // Normal map is chosen because almost every item has a normal map, diffuse is chosen otherwise var width = 0; var height = 0; var largestSize = 0; if (texMapData.Normal != null) { width = texMapData.Normal.Width; height = texMapData.Normal.Height; largestSize = width * height; } else if (texMapData.Diffuse != null) { width = texMapData.Diffuse.Width; height = texMapData.Diffuse.Height; largestSize = width * height; } var scaleDown = false; var scale = 1; if (texMapData.Normal != null) { var size = texMapData.Normal.Width * texMapData.Normal.Height; if (size > largestSize) { largestSize = size; width = texMapData.Normal.Width; height = texMapData.Normal.Height; } } if (texMapData.Diffuse != null) { var size = texMapData.Diffuse.Width * texMapData.Diffuse.Height; if (size > largestSize) { largestSize = size; width = texMapData.Diffuse.Width; height = texMapData.Diffuse.Height; } } if (texMapData.Specular != null) { var size = texMapData.Specular.Width * texMapData.Specular.Height; if (size > largestSize) { largestSize = size; width = texMapData.Specular.Width; height = texMapData.Specular.Height; } } if (width > 4000 || height > 4000) { scale = 4; scaleDown = true; } //else if (width > 2000 || height > 2000) //{ // scale = 2; // scaleDown = true; //} width = width / scale; height = height / scale; largestSize = width * height; await Task.Run(() => { if (texMapData.Normal != null && largestSize > texMapData.Normal.Width *texMapData.Normal.Height || scaleDown) { using (var img = Image.LoadPixelData <Rgba32>(texMapData.Normal.Data, texMapData.Normal.Width, texMapData.Normal.Height)) { // ImageSharp pre-multiplies the RGB by the alpha component during resize, if alpha is 0 (colourset row 0) // this ends up causing issues and destroying the RGB values resulting in an invisible preview model // https://github.com/SixLabors/ImageSharp/issues/1498#issuecomment-757519563 img.Mutate(x => x.Resize( new ResizeOptions { Size = new Size(width, height), PremultiplyAlpha = false }) ); texMapData.Normal.Data = MemoryMarshal.AsBytes(img.GetPixelMemoryGroup()[0].Span).ToArray(); } } if (texMapData.Diffuse != null && (largestSize > texMapData.Diffuse.Width *texMapData.Diffuse.Height || scaleDown)) { using (var img = Image.LoadPixelData <Rgba32>(texMapData.Diffuse.Data, texMapData.Diffuse.Width, texMapData.Diffuse.Height)) { for (int i = 0; i < img.Height; i++) { var pixelRowSpan = img.GetPixelRowSpan(i); for (int j = 0; j < img.Width; j++) { pixelRowSpan[j] = new Rgba32(pixelRowSpan[j].R, pixelRowSpan[j].G, pixelRowSpan[j].B, 255); } } img.Mutate(x => x.Resize(width, height)); texMapData.Diffuse.Data = MemoryMarshal.AsBytes(img.GetPixelMemoryGroup()[0].Span).ToArray(); } } if (texMapData.Specular != null && (largestSize > texMapData.Specular.Width *texMapData.Specular.Height || scaleDown)) { using (var img = Image.LoadPixelData <Rgba32>(texMapData.Specular.Data, texMapData.Specular.Width, texMapData.Specular.Height)) { img.Mutate(x => x.Resize(width, height)); texMapData.Specular.Data = MemoryMarshal.AsBytes(img.GetPixelMemoryGroup()[0].Span).ToArray(); } } }); return(width, height); }