From 29466c708eefa64e87fae04860285b7cffb45804 Mon Sep 17 00:00:00 2001 From: kaczy Date: Mon, 7 Aug 2023 10:48:33 +0200 Subject: [PATCH] Hues support --- ClassicUO.Assets/HuesLoader.cs | 336 +++++++++++++++++++++ ClassicUO.Assets/UOFileManager.cs | 1 + src/Map/Map.cs | 3 +- src/Map/MapManager.cs | 43 ++- src/Renderer/Effects/Shaders/MapEffect.fx | 19 ++ src/Renderer/Effects/Shaders/MapEffect.fxc | Bin 4948 -> 5496 bytes src/Renderer/MapRenderer.cs | 51 +++- src/UOGame.cs | 16 +- 8 files changed, 451 insertions(+), 18 deletions(-) create mode 100644 ClassicUO.Assets/HuesLoader.cs diff --git a/ClassicUO.Assets/HuesLoader.cs b/ClassicUO.Assets/HuesLoader.cs new file mode 100644 index 0000000..885c686 --- /dev/null +++ b/ClassicUO.Assets/HuesLoader.cs @@ -0,0 +1,336 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using ClassicUO.IO; +using ClassicUO.Utility; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace ClassicUO.Assets +{ + public class HuesLoader : UOFileLoader + { + private static HuesLoader _instance; + + private HuesLoader() + { + } + + public static HuesLoader Instance => _instance ?? (_instance = new HuesLoader()); + + public HuesGroup[] HuesRange { get; private set; } + + public int HuesCount { get; private set; } + + public FloatHues[] Palette { get; private set; } + + public ushort[] RadarCol { get; private set; } + + public override unsafe Task Load() + { + return Task.Run + ( + () => + { + string path = UOFileManager.GetUOFilePath("hues.mul"); + + FileSystemHelper.EnsureFileExists(path); + + UOFileMul file = new UOFileMul(path); + int groupSize = Marshal.SizeOf(); + int entrycount = (int) file.Length / groupSize; + HuesCount = entrycount * 8; + HuesRange = new HuesGroup[entrycount]; + ulong addr = (ulong) file.StartAddress; + + for (int i = 0; i < entrycount; i++) + { + HuesRange[i] = Marshal.PtrToStructure((IntPtr) (addr + (ulong) (i * groupSize))); + } + + path = UOFileManager.GetUOFilePath("radarcol.mul"); + + FileSystemHelper.EnsureFileExists(path); + + UOFileMul radarcol = new UOFileMul(path); + RadarCol = new ushort[(int)(radarcol.Length >> 1)]; + + fixed (ushort* ptr = RadarCol) + { + Unsafe.CopyBlockUnaligned((void*)(byte*)ptr, radarcol.PositionAddress.ToPointer(), (uint)radarcol.Length); + } + + file.Dispose(); + radarcol.Dispose(); + } + ); + } + + public float[] CreateHuesPalette() + { + float[] p = new float[32 * 3 * HuesCount]; + + Palette = new FloatHues[HuesCount]; + int entrycount = HuesCount >> 3; + + for (int i = 0; i < entrycount; i++) + { + for (int j = 0; j < 8; j++) + { + int idx = i * 8 + j; + + Palette[idx].Palette = new float[32 * 3]; + + for (int h = 0; h < 32; h++) + { + int idx1 = h * 3; + + ushort c = HuesRange[i].Entries[j].ColorTable[h]; + + Palette[idx].Palette[idx1] = ((c >> 10) & 0x1F) / 31.0f; + + Palette[idx].Palette[idx1 + 1] = ((c >> 5) & 0x1F) / 31.0f; + + Palette[idx].Palette[idx1 + 2] = (c & 0x1F) / 31.0f; + + p[idx * 96 + idx1 + 0] = Palette[idx].Palette[idx1]; + + p[idx * 96 + idx1 + 1] = Palette[idx].Palette[idx1 + 1]; + + p[idx * 96 + idx1 + 2] = Palette[idx].Palette[idx1 + 2]; + + //p[iddd++] = Palette[idx].Palette[idx1]; + //p[iddd++] = Palette[idx].Palette[idx1 + 1]; + //p[iddd++] = Palette[idx].Palette[idx1 + 2]; + } + } + } + + return p; + } + + public void CreateShaderColors(uint[] buffer) + { + int len = HuesRange.Length; + + int idx = 0; + + for (int r = 0; r < len; r++) + { + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 32; x++) + { + buffer[idx++] = HuesHelper.Color16To32(HuesRange[r].Entries[y].ColorTable[x]) | 0xFF_00_00_00; + + if (idx >= buffer.Length) + { + return; + } + } + } + } + } + + //public float[] GetColorForShader(ushort color) + //{ + // if (color != 0) + // { + // if (color >= HuesCount) + // { + // color %= (ushort)HuesCount; + + // if (color <= 0) + // color = 1; + // } + + // return Palette[color - 1].Palette; + // } + + // return _empty; + //} + + //public static void SetHuesBlock(int index, IntPtr ptr) + //{ + // VerdataHuesGroup group = Marshal.PtrToStructure(ptr); + // SetHuesBlock(index, group); + //} + + //public static void SetHuesBlock(int index, VerdataHuesGroup group) + //{ + // if (index < 0 || index >= HuesCount) + // return; + + // HuesRange[index].Header = group.Header; + // for (int i = 0; i < 8; i++) HuesRange[index].Entries[i].ColorTable = group.Entries[i].ColorTable; + //} + + public ushort GetColor16(ushort c, ushort color) + { + if (color != 0 && color < HuesCount) + { + color -= 1; + int g = color >> 3; + int e = color % 8; + + return HuesRange[g].Entries[e].ColorTable[(c >> 10) & 0x1F]; + } + + return c; + } + + public uint GetPolygoneColor(ushort c, ushort color) + { + if (color != 0 && color < HuesCount) + { + color -= 1; + int g = color >> 3; + int e = color % 8; + + return HuesHelper.Color16To32(HuesRange[g].Entries[e].ColorTable[c]); + } + + return 0xFF010101; + } + + public uint GetUnicodeFontColor(ushort c, ushort color) + { + if (color != 0 && color < HuesCount) + { + color -= 1; + int g = color >> 3; + int e = color % 8; + + return HuesRange[g].Entries[e].ColorTable[8]; + } + + return HuesHelper.Color16To32(c); + } + + public uint GetColor(ushort c, ushort color) + { + if (color != 0 && color < HuesCount) + { + color -= 1; + int g = color >> 3; + int e = color % 8; + + return HuesHelper.Color16To32(HuesRange[g].Entries[e].ColorTable[(c >> 10) & 0x1F]); + } + + return color != 0 ? HuesHelper.Color16To32(color) : HuesHelper.Color16To32(c); + } + + public uint GetPartialHueColor(ushort c, ushort color) + { + if (color != 0 && color < HuesCount) + { + color -= 1; + int g = color >> 3; + int e = color % 8; + uint cl = HuesHelper.Color16To32(c); + + byte R = (byte) (cl & 0xFF); + byte G = (byte) ((cl >> 8) & 0xFF); + byte B = (byte) ((cl >> 16) & 0xFF); + + if (R == G && R == B) + { + cl = HuesHelper.Color16To32(HuesRange[g].Entries[e].ColorTable[(c >> 10) & 0x1F]); + } + + return cl; + } + + return HuesHelper.Color16To32(c); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort GetRadarColorData(int c) + { + if (c >= 0 && c < RadarCol.Length) + { + return RadarCol[c]; + } + + return 0; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct HuesBlock + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public ushort[] ColorTable; + public ushort TableStart; + public ushort TableEnd; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public char[] Name; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct HuesGroup + { + public uint Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public HuesBlock[] Entries; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public readonly struct VerdataHuesBlock + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly ushort[] ColorTable; + public readonly ushort TableStart; + public readonly ushort TableEnd; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly char[] Name; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly ushort[] Unk; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public readonly struct VerdataHuesGroup + { + public readonly uint Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly VerdataHuesBlock[] Entries; + } + + public struct FloatHues + { + //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 96)] + public float[] Palette; + } +} \ No newline at end of file diff --git a/ClassicUO.Assets/UOFileManager.cs b/ClassicUO.Assets/UOFileManager.cs index 4a37dde..320c73a 100644 --- a/ClassicUO.Assets/UOFileManager.cs +++ b/ClassicUO.Assets/UOFileManager.cs @@ -102,6 +102,7 @@ public static void Load(ClientVersion version, string basePath, bool useVerdata, ArtLoader.Instance.Load(), TileDataLoader.Instance.Load(), TexmapsLoader.Instance.Load(), + HuesLoader.Instance.Load(), }; if (!Task.WhenAll(tasks).Wait(TimeSpan.FromSeconds(10))) diff --git a/src/Map/Map.cs b/src/Map/Map.cs index e30a2f3..7491494 100644 --- a/src/Map/Map.cs +++ b/src/Map/Map.cs @@ -226,7 +226,7 @@ private bool CanDrawStatic(ushort id) var offsetX = m_Statics.ReadByte(); var offsetY = m_Statics.ReadByte(); var offsetZ = m_Statics.ReadSByte(); - m_Statics.ReadUInt16(); + var hue = m_Statics.ReadUInt16(); if (!CanDrawStatic(id)) continue; @@ -247,6 +247,7 @@ private bool CanDrawStatic(ushort id) tile.X = (x * 8) + offsetX; tile.Y = (y * 8) + offsetY; tile.Z = offsetZ; + tile.Hue = hue; } if (x == 180 && y == 211) diff --git a/src/Map/MapManager.cs b/src/Map/MapManager.cs index 523e622..b460bcb 100644 --- a/src/Map/MapManager.cs +++ b/src/Map/MapManager.cs @@ -521,7 +521,7 @@ private void DrawShadowMap(int minTileX, int minTileY, int maxTileX, int maxTile if (!IsRock(s.ID) && !IsTree(s.ID) && !data.Flags.HasFlag(TileFlag.Foliage)) continue; - DrawStatic(ref s, x, y, (statics.Length - 1 - i) * 0.001f); + DrawStatic(ref s, x, y, (statics.Length - 1 - i) * 0.0001f); } } } @@ -567,6 +567,42 @@ private bool CanDrawStatic(ushort id) return true; } + private enum HueMode + { + NONE = 0, + HUED = 1, + PARTIAL = 2 + } + + private Vector3 GetHueVector(StaticTile s) { + var hue = s.Hue; + var partial = TileDataLoader.Instance.StaticData[s.ID].IsPartialHue; + HueMode mode; + + if ((s.Hue & 0x8000) != 0) + { + partial = true; + hue &= 0x7FFF; + } + + if (hue == 0) + { + partial = false; + } + + if (hue != 0) { + // hue -= 1; //ClassicUO does this decrement, but I works without it + + mode = partial ? HueMode.PARTIAL : HueMode.HUED; + } + else + { + mode = HueMode.NONE; + } + + return new Vector3(hue, (int)mode, 0); + } + private void DrawStatic(ref StaticTile s, int x, int y, float depthOffset) { if (!CanDrawStatic(s.ID)) @@ -590,11 +626,14 @@ private void DrawStatic(ref StaticTile s, int x, int y, float depthOffset) bool cylindrical = data.Flags.HasFlag(TileFlag.Foliage) || IsRock(s.ID) || IsTree(s.ID); + var hueCoords = GetHueVector(s); + _mapRenderer.DrawBillboard( new Vector3(x * TILE_SIZE, y * TILE_SIZE, s.Z * TILE_Z_SCALE), depthOffset, staticTex.Texture, staticTex.Bounds, + hueCoords, cylindrical ); } @@ -611,7 +650,7 @@ public void DrawStatics(int minTileX, int minTileY, int maxTileX, int maxTileY) { ref var s = ref statics[i]; - DrawStatic(ref s, x, y, (statics.Length - 1 - i) * 0.001f); + DrawStatic(ref s, x, y, (statics.Length - 1 - i) * 0.0001f); } } } diff --git a/src/Renderer/Effects/Shaders/MapEffect.fx b/src/Renderer/Effects/Shaders/MapEffect.fx index d124809..1c87c1d 100644 --- a/src/Renderer/Effects/Shaders/MapEffect.fx +++ b/src/Renderer/Effects/Shaders/MapEffect.fx @@ -1,5 +1,12 @@ +#define NONE 0 +#define HUED 1 +#define PARTIAL 2 + +static const float HuesPerTexture = 3000; + sampler TextureSampler : register(s0); sampler ShadowSampler : register(s1); +sampler HueSampler : register(s2); cbuffer ProjectionMatrix : register(b0) { float4x4 WorldViewProj; @@ -20,6 +27,7 @@ struct VSInput { float4 Position : SV_Position; float3 Normal : NORMAL; float3 TexCoord : TEXCOORD0; + float3 HueCoord : TEXCOORD1; }; /* Terrain/Land */ @@ -98,6 +106,7 @@ struct StaticsVSOutput { float4 LightViewPosition : TEXCOORD2; float3 TexCoord : TEXCOORD3; float3 Normal : TEXCOORD4; + float3 HueCoord : TEXCOORD5; }; struct StaticsPSInput { @@ -106,6 +115,7 @@ struct StaticsPSInput { float4 LightViewPosition : TEXCOORD2; float3 TexCoord : TEXCOORD3; float3 Normal : TEXCOORD4; + float3 HueCoord : TEXCOORD5; }; StaticsVSOutput StaticsVSMain(VSInput vin) { @@ -116,6 +126,7 @@ StaticsVSOutput StaticsVSMain(VSInput vin) { vout.LightViewPosition = mul(vin.Position, LightWorldViewProj); vout.Normal = vin.Normal; vout.TexCoord = vin.TexCoord; + vout.HueCoord = vin.HueCoord; //vout.ScreenPosition.z -= (vin.Position.z / 512.0f) * 0.001f; vout.ScreenPosition.z += vin.TexCoord.z; @@ -131,6 +142,14 @@ float4 StaticsPSMain(StaticsPSInput pin) : SV_Target0 if (color.a == 0) discard; + + int mode = int(pin.HueCoord.y); + + if (mode == HUED || (mode == PARTIAL && color.r == color.g && color.r == color.b)) + { + float2 hueCoord = float2(color.r, pin.HueCoord.x / HuesPerTexture); + color.rgb = tex2D(HueSampler, hueCoord).rgb; + } float2 LightViewTexCoords; LightViewTexCoords.x = (pin.LightViewPosition.x / pin.LightViewPosition.w) / 2.0f + 0.5f; diff --git a/src/Renderer/Effects/Shaders/MapEffect.fxc b/src/Renderer/Effects/Shaders/MapEffect.fxc index a9ded0d03b8134fa8077b122ad5ffa2077d0e929..15954d727365ad3bdbb89bd85934743ea24b200c 100644 GIT binary patch delta 1367 zcmY*YO-L0{6h8OPjPEzJOehfyDv1j1k07wbpT*my)PRd11%(z#S_;zUUBN|*etcbb zZAOxkA!6dS34($)1uX?zBnZv2MT@s+A&%dyTd1IRI0%De*q z0&E$^hnY{A*)|5`4lHHP!=J7Hus39N%>heB(M~i+gSYz!IuO3)Okg=YIL0~Q0b7Fu zC8qFaJb%J|8%!E(JHg7}GQwq2tjl250c=RnCA{i6AJp)($AN$O99o+?)2Ipli%=qRXJwc-|nz;=UCyeG(DMGFr3;6+}F@jYXL zT!lSZoD=ihU_W*lOYTh_g%2Ken9s3W<)4ILD#KhtyrbC^; z#%7z$MkR)furaR9gP5TMyBjOU7XZM=YJ89FAj$oJm4UNw@8cMvl8x@g;71<*sg?~s-(vveaD9&b z@qpEXbf5_SWjrya%K;B@pRSu@=0Yfpg@{2*ctX#Yj$(=R=cRUG czF3EZvUKgpP2DJ~0`J?Q+?G|JIrPWb1$(gPCjbBd delta 362 zcmeyNbw!Ppk@Nq*nu)COoXiXi3`{`0W8&0&Mvcj=jKz#OlPejW8P`nS%jnMd1xU(I zR$q<1H_Y|{1ZU- z5g^t8xfcjP8WkolWD!>V0u=p#q>=}yf&++ikW{9CREk;vMNEJgW@Z48?X#JY^*Yn$ d4g5ElC%5p5FuvG4S2&$l<{A7CSOMVHIBNg^ diff --git a/src/Renderer/MapRenderer.cs b/src/Renderer/MapRenderer.cs index bd67f9b..edb66eb 100644 --- a/src/Renderer/MapRenderer.cs +++ b/src/Renderer/MapRenderer.cs @@ -26,6 +26,7 @@ VertexDeclaration IVertexType.VertexDeclaration { public Vector3 Position; public Vector3 Normal; public Vector3 TextureCoordinate; + public Vector3 HueCoordinate; public static readonly VertexDeclaration VertexDeclaration; @@ -51,6 +52,12 @@ static MapVertex() VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 0 + ), + new VertexElement( + 36, + VertexElementFormat.Vector3, + VertexElementUsage.TextureCoordinate, + 0 ) } ); @@ -59,12 +66,14 @@ static MapVertex() public MapVertex( Vector3 position, Vector3 normal, - Vector3 textureCoordinate + Vector3 textureCoordinate, + Vector3 hueCoordinate ) { Position = position; Normal = normal; TextureCoordinate = textureCoordinate; + HueCoordinate = hueCoordinate; } } @@ -258,19 +267,23 @@ public void DrawTile( _vertexInfo[cur++] = new MapVertex( new Vector3(posX, posY, cornerZ.X), normal0, - new Vector3(texX + (texWidth / 2f), texY, 0)); + new Vector3(texX + (texWidth / 2f), texY, 0), + Vector3.Zero); _vertexInfo[cur++] = new MapVertex( new Vector3(posX + TILE_SIZE, posY, cornerZ.Y), normal1, - new Vector3(texX + texWidth, texY + (texHeight / 2f), 0)); + new Vector3(texX + texWidth, texY + (texHeight / 2f), 0), + Vector3.Zero); _vertexInfo[cur++] = new MapVertex( new Vector3(posX, posY + TILE_SIZE, cornerZ.Z), normal2, - new Vector3(texX, texY + (texHeight / 2f), 0)); + new Vector3(texX, texY + (texHeight / 2f), 0), + Vector3.Zero); _vertexInfo[cur++] = new MapVertex( new Vector3(posX + TILE_SIZE, posY + TILE_SIZE, cornerZ.W), normal3, - new Vector3(texX + (texWidth / 2f), texY + texHeight, 0)); + new Vector3(texX + (texWidth / 2f), texY + texHeight, 0), + Vector3.Zero); } else { @@ -278,19 +291,23 @@ public void DrawTile( _vertexInfo[cur++] = new MapVertex( new Vector3(posX, posY, cornerZ.X), normal0, - new Vector3(texX, texY, 0)); + new Vector3(texX, texY, 0), + Vector3.Zero); _vertexInfo[cur++] = new MapVertex( new Vector3(posX + TILE_SIZE, posY, cornerZ.Y), normal1, - new Vector3(texX + texWidth, texY, 0)); + new Vector3(texX + texWidth, texY, 0), + Vector3.Zero); _vertexInfo[cur++] = new MapVertex( new Vector3(posX, posY + TILE_SIZE, cornerZ.Z), normal2, - new Vector3(texX, texY + texHeight, 0)); + new Vector3(texX, texY + texHeight, 0), + Vector3.Zero); _vertexInfo[cur++] = new MapVertex( new Vector3(posX + TILE_SIZE, posY + TILE_SIZE, cornerZ.W), normal3, - new Vector3(texX + texWidth, texY + texHeight, 0)); + new Vector3(texX + texWidth, texY + texHeight, 0), + Vector3.Zero); } _numTiles++; @@ -300,6 +317,7 @@ public void DrawBillboard( Vector3 tilePos, float depthOffset, Rectangle texCoords, + Vector3 hueCoords, bool cylindrical) { @@ -355,19 +373,23 @@ public void DrawBillboard( _vertexInfo[cur++] = new MapVertex( Vector3.Transform(v1, vtrans), Vector3.UnitZ, - Vector3.Transform(t1, ttrans)); + Vector3.Transform(t1, ttrans), + hueCoords); _vertexInfo[cur++] = new MapVertex( Vector3.Transform(v2, vtrans), Vector3.UnitZ, - Vector3.Transform(t2, ttrans)); + Vector3.Transform(t2, ttrans), + hueCoords); _vertexInfo[cur++] = new MapVertex( Vector3.Transform(v3, vtrans), Vector3.UnitZ, - Vector3.Transform(t3, ttrans)); + Vector3.Transform(t3, ttrans), + hueCoords); _vertexInfo[cur++] = new MapVertex( Vector3.Transform(v4, vtrans), Vector3.UnitZ, - Vector3.Transform(t4, ttrans)); + Vector3.Transform(t4, ttrans), + hueCoords); _numTiles++; } @@ -497,10 +519,11 @@ public void DrawBillboard( float depthOffset, Texture2D texture, Rectangle texCoords, + Vector3 hueCoords, bool cylindrical) { var batcher = GetBatcher(texture); - batcher.DrawBillboard(tilePos, depthOffset, texCoords, cylindrical); + batcher.DrawBillboard(tilePos, depthOffset, texCoords, hueCoords, cylindrical); } } diff --git a/src/UOGame.cs b/src/UOGame.cs index 2553812..e2d73b5 100644 --- a/src/UOGame.cs +++ b/src/UOGame.cs @@ -28,7 +28,7 @@ public UOGame() IsMouseVisible = true; } - protected override void Initialize() + protected override unsafe void Initialize() { Log.Start(LogTypes.All); @@ -40,6 +40,20 @@ protected override void Initialize() } UOFileManager.Load(clientVersion, UORenderer.CurrentProject.BasePath, false, "ENU"); + + const int TEXTURE_WIDTH = 32; + const int TEXTURE_HEIGHT = 3000; + + var hueSampler = new Texture2D(GraphicsDevice, TEXTURE_WIDTH, TEXTURE_HEIGHT); + uint[] buffer = System.Buffers.ArrayPool.Shared.Rent(TEXTURE_WIDTH * TEXTURE_HEIGHT); + + fixed (uint* ptr = buffer) { + HuesLoader.Instance.CreateShaderColors(buffer); + hueSampler.SetDataPointerEXT(0, null, (IntPtr)ptr, TEXTURE_WIDTH * TEXTURE_HEIGHT * sizeof(uint)); + } + System.Buffers.ArrayPool.Shared.Return(buffer); + GraphicsDevice.Textures[2] = hueSampler; + GraphicsDevice.SamplerStates[2] = SamplerState.PointClamp; if (_gdm.GraphicsDevice.Adapter.IsProfileSupported(GraphicsProfile.HiDef)) {