From e9dca50c2d17184299400e6c2aa7c095f288552c Mon Sep 17 00:00:00 2001 From: jracek Date: Tue, 11 Mar 2025 22:54:49 +0100 Subject: [PATCH] Exporting the finished VRAM, loads correctly within pcsx-redux, Refactor of PSXTexture2D --- Editor/VramEditorWindow.cs | 48 +++--- Runtime/PSXTexture2D.cs | 154 +++++++++--------- Runtime/TexturePacker.cs | 311 +++++++++++++++++++++---------------- 3 files changed, 271 insertions(+), 242 deletions(-) diff --git a/Editor/VramEditorWindow.cs b/Editor/VramEditorWindow.cs index 641773e..bb57fb3 100644 --- a/Editor/VramEditorWindow.cs +++ b/Editor/VramEditorWindow.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; -using System.Linq; -using System.Reflection.Emit; +using System.IO; using PSXSplash.RuntimeCode; using Unity.Collections; using UnityEditor; @@ -61,7 +60,6 @@ public class VRAMEditorWindow : EditorWindow return; } - // Get pixels from the overlay texture Color[] overlayPixels = overlayTexture.GetPixels(); Color[] basePixels = baseTexture.GetPixels(); @@ -70,31 +68,25 @@ public class VRAMEditorWindow : EditorWindow int overlayWidth = overlayTexture.width; int overlayHeight = overlayTexture.height; - // Loop through the overlay texture and paste it onto the base texture for (int y = 0; y < overlayHeight; y++) { for (int x = 0; x < overlayWidth; x++) { int baseX = posX + x; - int baseY = posY + y; - - // Ensure we are within bounds of the base texture + int baseY = posY + y; if (baseX >= 0 && baseX < baseWidth && baseY >= 0 && baseY < baseHeight) { int baseIndex = baseY * baseWidth + baseX; int overlayIndex = y * overlayWidth + x; - // Blend or replace pixel (simple overwrite in this case) basePixels[baseIndex] = overlayPixels[overlayIndex]; } } } - // Apply the modified pixels back to the base texture baseTexture.SetPixels(basePixels); baseTexture.Apply(); } - private void PackTextures() { @@ -122,28 +114,36 @@ public class VRAMEditorWindow : EditorWindow dontPackAreas.Add(buffer1); - dontPackAreas.Add(buffer2); + if (dualBuffering) + { + dontPackAreas.Add(buffer2); + } VRAMPacker tp = new VRAMPacker(dontPackAreas); var packed = tp.PackTexturesIntoVRAM(objects); - foreach (TextureAtlas ta in packed.atlases) + for (int y = 0; y < VramHeight; y++) { - foreach (PSXTexture2D texture in ta.ContainedTextures) + for (int x = 0; x < VramWidth; x++) { - Debug.Log($"Packing {texture} at: x:{ta.PositionX + texture.PackingX} y:{ta.PositionY + texture.PackingY}"); - PasteTexture(vramImage, texture.GenerateVramPreview(), ta.PositionX + texture.PackingX, ta.PositionY + texture.PackingY); - Debug.Log($"Texpage: {texture.TexpageNum} Offset:({texture.PackingX},{texture.PackingY})"); - if (texture.BitDepth != PSXBPP.TEX_16BIT) + vramImage.SetPixel(x, VramHeight - y - 1, packed._vramPixels[x, y].GetUnityColor()); + } + } + vramImage.Apply(); + + string path = EditorUtility.SaveFilePanel("Select Output File", "", "output", "bin"); + + using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create))) + { + for (int y = 0; y < VramHeight; y++) + { + for (int x = 0; x < VramWidth; x++) { - for (int i = 0; i < texture.ColorPalette.Count; i++) - { - vramImage.SetPixel(texture.ClutPackingX + i, texture.ClutPackingY, texture.ColorPalette[i].GetUnityColor()); - } - vramImage.Apply(); + writer.Write(packed._vramPixels[x, y].Pack()); } } } + } private void OnGUI() @@ -220,12 +220,12 @@ public class VRAMEditorWindow : EditorWindow : new Rect(vramRect.x + selectedResolution.x, vramRect.y, selectedResolution.x, selectedResolution.y); EditorGUI.DrawRect(buffer1, bufferColor1); - GUI.Label(new Rect(buffer1.center.x - 40, buffer1.center.y - 10, 80, 20), "Framebuffer A", EditorStyles.boldLabel); + GUI.Label(new Rect(buffer1.center.x - 40, buffer1.center.y - 10, 120, 20), "Framebuffer A", EditorStyles.boldLabel); GUILayout.Space(10); if (dualBuffering) { EditorGUI.DrawRect(buffer2, bufferColor2); - GUI.Label(new Rect(buffer2.center.x - 40, buffer2.center.y - 10, 80, 20), "Framebuffer B", EditorStyles.boldLabel); + GUI.Label(new Rect(buffer2.center.x - 40, buffer2.center.y - 10, 120, 20), "Framebuffer B", EditorStyles.boldLabel); } foreach (ProhibitedArea area in prohibitedAreas) diff --git a/Runtime/PSXTexture2D.cs b/Runtime/PSXTexture2D.cs index 667cc41..35a895a 100644 --- a/Runtime/PSXTexture2D.cs +++ b/Runtime/PSXTexture2D.cs @@ -64,7 +64,7 @@ namespace PSXSplash.RuntimeCode /// The packed ushort value. public ushort Pack() { - return (ushort)((r << 11) | (g << 6) | (b << 1) | (SemiTransparent ? 1 : 0)); + return (ushort)((SemiTransparent ? 1 << 15 : 0) | (b << 10) | (g << 5) | r); } /// @@ -79,7 +79,8 @@ namespace PSXSplash.RuntimeCode SemiTransparent = (packedValue & 0b1) != 0; } - public Color GetUnityColor() { + public Color GetUnityColor() + { return new Color(R / 31.0f, G / 31.0f, B / 31.0f); } } @@ -92,10 +93,11 @@ namespace PSXSplash.RuntimeCode public int Width { get; set; } public int QuantizedWidth { get; set; } public int Height { get; set; } - public int[] PixelIndices { get; set; } + public int[,] PixelIndices { get; set; } public List ColorPalette = new List(); public PSXBPP BitDepth { get; set; } + public Texture2D OriginalTexture; // Within supertexture @@ -107,11 +109,10 @@ namespace PSXSplash.RuntimeCode // Absolute positioning public int ClutPackingX; public int ClutPackingY; - + private int _maxColors; - // Used only for 16bpp - public ushort[] ImageData { get; set; } + public VRAMPixel[,] ImageData { get; set; } /// /// Creates a PSX texture from a given Texture2D with the specified bit depth. @@ -128,19 +129,30 @@ namespace PSXSplash.RuntimeCode bitDepth == PSXBPP.TEX_8BIT ? inputTexture.width / 2 : inputTexture.width; psxTex.Height = inputTexture.height; - + psxTex.BitDepth = bitDepth; if (bitDepth == PSXBPP.TEX_16BIT) { - psxTex.ImageData = new ushort[inputTexture.width * inputTexture.height]; - int i = 0; - foreach (Color pixel in inputTexture.GetPixels()) + psxTex.ImageData = new VRAMPixel[inputTexture.width, inputTexture.height]; + + int width = inputTexture.width; + int height = inputTexture.height; + + for (int y = 0; y < height; y++) // Start from top row, move downward { - VRAMPixel vramPixel = new VRAMPixel { R = (ushort)(pixel.r * 31), G = (ushort)(pixel.g * 31), B = (ushort)(pixel.b * 31) }; - psxTex.ImageData[i] = vramPixel.Pack(); - i++; + for (int x = 0; x < width; x++) // Start from right column, move leftward + { + Color pixel = inputTexture.GetPixel(x, height-y-1); + VRAMPixel vramPixel = new VRAMPixel + { + R = (ushort)(pixel.r * 31), + G = (ushort)(pixel.g * 31), + B = (ushort)(pixel.b * 31) + }; + psxTex.ImageData[x, y] = vramPixel; + } } psxTex.ColorPalette = null; return psxTex; @@ -158,12 +170,43 @@ namespace PSXSplash.RuntimeCode } - psxTex.PixelIndices = new int[psxTex.Width * psxTex.Height]; - for (int x = 0; x < psxTex.Width; x++) + psxTex.ImageData = new VRAMPixel[psxTex.QuantizedWidth, psxTex.Height]; + + psxTex.PixelIndices = result.Indices; + + int groupSize = (bitDepth == PSXBPP.TEX_8BIT) ? 2 : 4; + + for (int y = 0; y < psxTex.Height; y++) { - for (int y = 0; y < psxTex.Height; y++) + if (bitDepth == PSXBPP.TEX_8BIT) { - psxTex.PixelIndices[x + y * psxTex.Width] = result.Indices[x, y]; + for (int group = 0; group < psxTex.QuantizedWidth; group++) + { + int baseIndex = group * 2; + // Combine two 8-bit indices into one ushort. + int index1 = psxTex.PixelIndices[baseIndex, y] & 0xFF; + int index2 = psxTex.PixelIndices[baseIndex + 1, y] & 0xFF; + ushort packed = (ushort)((index1 << 8) | index2); + VRAMPixel pixel = new VRAMPixel(); + pixel.Unpack(packed); + psxTex.ImageData[group, psxTex.Height-y-1] = pixel; + } + } + else if (bitDepth == PSXBPP.TEX_4BIT) + { + for (int group = 0; group < psxTex.QuantizedWidth; group++) + { + int baseIndex = group * 4; + // Combine four 4-bit indices into one ushort. + int idx1 = psxTex.PixelIndices[baseIndex, y] & 0xF; + int idx2 = psxTex.PixelIndices[baseIndex + 1, y] & 0xF; + int idx3 = psxTex.PixelIndices[baseIndex + 2, y] & 0xF; + int idx4 = psxTex.PixelIndices[baseIndex + 3, y] & 0xF; + ushort packed = (ushort)((idx1 << 12) | (idx2 << 8) | (idx3 << 4) | idx4); + VRAMPixel pixel = new VRAMPixel(); + pixel.Unpack(packed); + psxTex.ImageData[group, psxTex.Height-y-1] = pixel; + } } } @@ -181,42 +224,26 @@ namespace PSXSplash.RuntimeCode Texture2D tex = new Texture2D(Width, Height); if (BitDepth == PSXBPP.TEX_16BIT) { - Color[] colors16 = new Color[Width * Height]; - // An instance for the Unpack method - VRAMPixel pixel = new VRAMPixel(); - for (int i = 0; i < ImageData.Length; i++) + for (int y = 0; y < Width; y++) { - ushort packedValue = ImageData[i]; - pixel.Unpack(packedValue); - float r = pixel.R / 31f; - float g = pixel.G / 31f; - float b = pixel.B / 31f; - - colors16[i] = new Color(r, g, b); + for (int x = 0; x < Height; x++) + { + tex.SetPixel(x, y, ImageData[x, y].GetUnityColor()); + } } - tex.SetPixels(colors16); + tex.Apply(); return tex; } - - List colors = new List(); for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { - int pixel = PixelIndices[y * Width + x]; - VRAMPixel color = ColorPalette[pixel]; - - float r = color.R / 31f; - float g = color.G / 31f; - float b = color.B / 31f; - - colors.Add(new Color(r, g, b)); + tex.SetPixel(x, y, ColorPalette[PixelIndices[x, y]].GetUnityColor()); } } - tex.SetPixels(colors.ToArray()); tex.Apply(); return tex; } @@ -233,51 +260,16 @@ namespace PSXSplash.RuntimeCode return GeneratePreview(); } - int adjustedWidth = Width; - - if (BitDepth == PSXBPP.TEX_4BIT) - { - adjustedWidth = Mathf.CeilToInt(Width / 4f); - } - else if (BitDepth == PSXBPP.TEX_8BIT) - { - adjustedWidth = Mathf.CeilToInt(Width / 2f); - } - - Texture2D vramTexture = new Texture2D(adjustedWidth, Height); - - List packedValues = new List(); - - if (BitDepth == PSXBPP.TEX_4BIT) - { - for (int i = 0; i < PixelIndices.Length; i += 4) - { - ushort packed = (ushort)((PixelIndices[i] << 12) | (PixelIndices[i + 1] << 8) | (PixelIndices[i + 2] << 4) | PixelIndices[i + 3]); - packedValues.Add(packed); - } - } - else if (BitDepth == PSXBPP.TEX_8BIT) - { - for (int i = 0; i < PixelIndices.Length; i += 2) - { - ushort packed = (ushort)((PixelIndices[i] << 8) | PixelIndices[i + 1]); - packedValues.Add(packed); - } - } - + Texture2D vramTexture = new Texture2D(QuantizedWidth, Height); List colors = new List(); - for (int i = 0; i < packedValues.Count; i++) + for (int y = 0; y < Height; y++) { - int index = packedValues[i]; - - float r = (index & 31) / 31.0f; - float g = ((index >> 5) & 31) / 31.0f; - float b = ((index >> 10) & 31) / 31.0f; - - colors.Add(new Color(r, g, b)); + for (int x = 0; x < QuantizedWidth; x++) + { + vramTexture.SetPixel(x, y, ImageData[x, y].GetUnityColor()); + } } - vramTexture.SetPixels(colors.ToArray()); vramTexture.Apply(); return vramTexture; diff --git a/Runtime/TexturePacker.cs b/Runtime/TexturePacker.cs index e84025e..b9f45d6 100644 --- a/Runtime/TexturePacker.cs +++ b/Runtime/TexturePacker.cs @@ -3,189 +3,226 @@ using System.Linq; using UnityEngine; using PSXSplash.RuntimeCode; -public class TextureAtlas + + +namespace PSXSplash.RuntimeCode { - public PSXBPP BitDepth; - public int PositionX; - public int PositionY; - public int Width; - public const int Height = 256; - public List ContainedTextures = new List(); -} -public class VRAMPacker -{ - private List _textureAtlases = new List(); - private List _reservedAreas; - private List _finalizedAtlases = new List(); - private List _allocatedCLUTs = new List(); - - private const int VRAM_WIDTH = 1024; - private const int VRAM_HEIGHT = 512; - - public VRAMPacker(List reservedAreas) + public class TextureAtlas { - _reservedAreas = reservedAreas; + public PSXBPP BitDepth; + public int PositionX; + public int PositionY; + public int Width; + public const int Height = 256; + public List ContainedTextures = new List(); } - public (PSXObjectExporter[] processedObjects, List atlases) PackTexturesIntoVRAM(PSXObjectExporter[] objects) + public class VRAMPacker { - List uniqueTextures = new List(); - var groupedObjects = objects.GroupBy(obj => obj.Texture.BitDepth).OrderByDescending(g => g.Key); + private List _textureAtlases = new List(); + private List _reservedAreas; + private List _finalizedAtlases = new List(); + private List _allocatedCLUTs = new List(); - foreach (var group in groupedObjects) + private const int VRAM_WIDTH = 1024; + private const int VRAM_HEIGHT = 512; + + private VRAMPixel[,] _vramPixels; + + public VRAMPacker(List reservedAreas) { - int atlasWidth = group.Key switch + _reservedAreas = reservedAreas; + _vramPixels = new VRAMPixel[VRAM_WIDTH, VRAM_HEIGHT]; + } + + public (PSXObjectExporter[] processedObjects, VRAMPixel[,] _vramPixels) PackTexturesIntoVRAM(PSXObjectExporter[] objects) + { + List uniqueTextures = new List(); + var groupedObjects = objects.GroupBy(obj => obj.Texture.BitDepth).OrderByDescending(g => g.Key); + + foreach (var group in groupedObjects) { - PSXBPP.TEX_16BIT => 256, - PSXBPP.TEX_8BIT => 128, - PSXBPP.TEX_4BIT => 64, - _ => 256 - }; - - TextureAtlas atlas = new TextureAtlas { BitDepth = group.Key, Width = atlasWidth, PositionX = 0, PositionY = 0 }; - _textureAtlases.Add(atlas); - - foreach (var obj in group.OrderByDescending(obj => obj.Texture.QuantizedWidth * obj.Texture.Height)) - { - if (uniqueTextures.Any(tex => tex.OriginalTexture.GetInstanceID() == obj.Texture.OriginalTexture.GetInstanceID() && tex.BitDepth == obj.Texture.BitDepth)) + int atlasWidth = group.Key switch { - obj.Texture = uniqueTextures.First(tex => tex.OriginalTexture.GetInstanceID() == obj.Texture.OriginalTexture.GetInstanceID()); - continue; - } + PSXBPP.TEX_16BIT => 256, + PSXBPP.TEX_8BIT => 128, + PSXBPP.TEX_4BIT => 64, + _ => 256 + }; - if (!TryPlaceTextureInAtlas(atlas, obj.Texture)) + TextureAtlas atlas = new TextureAtlas { BitDepth = group.Key, Width = atlasWidth, PositionX = 0, PositionY = 0 }; + _textureAtlases.Add(atlas); + + foreach (var obj in group.OrderByDescending(obj => obj.Texture.QuantizedWidth * obj.Texture.Height)) { - atlas = new TextureAtlas { BitDepth = group.Key, Width = atlasWidth, PositionX = 0, PositionY = 0 }; - _textureAtlases.Add(atlas); + /*if (uniqueTextures.Any(tex => tex.OriginalTexture.GetInstanceID() == obj.Texture.OriginalTexture.GetInstanceID() && tex.BitDepth == obj.Texture.BitDepth)) + { + obj.Texture = uniqueTextures.First(tex => tex.OriginalTexture.GetInstanceID() == obj.Texture.OriginalTexture.GetInstanceID()); + continue; + }*/ + if (!TryPlaceTextureInAtlas(atlas, obj.Texture)) { - Debug.LogError($"Failed to pack texture {obj.Texture}. It might not fit."); - break; + atlas = new TextureAtlas { BitDepth = group.Key, Width = atlasWidth, PositionX = 0, PositionY = 0 }; + _textureAtlases.Add(atlas); + if (!TryPlaceTextureInAtlas(atlas, obj.Texture)) + { + Debug.LogError($"Failed to pack texture {obj.Texture}. It might not fit."); + break; + } + } + uniqueTextures.Add(obj.Texture); + } + } + + ArrangeAtlasesInVRAM(); + AllocateCLUTs(); + BuildVram(); + return (objects, _vramPixels); + } + + private bool TryPlaceTextureInAtlas(TextureAtlas atlas, PSXTexture2D texture) + { + for (int y = 0; y <= TextureAtlas.Height - texture.Height; y++) + { + for (int x = 0; x <= atlas.Width - texture.QuantizedWidth; x++) + { + var candidateRect = new Rect(x, y, texture.QuantizedWidth, texture.Height); + if (!atlas.ContainedTextures.Any(tex => new Rect(tex.PackingX, tex.PackingY, tex.QuantizedWidth, tex.Height).Overlaps(candidateRect))) + { + texture.PackingX = x; + texture.PackingY = y; + atlas.ContainedTextures.Add(texture); + return true; } } - uniqueTextures.Add(obj.Texture); } + return false; } - ArrangeAtlasesInVRAM(); - AllocateCLUTs(); - return (objects, _finalizedAtlases); - } - - private bool TryPlaceTextureInAtlas(TextureAtlas atlas, PSXTexture2D texture) - { - for (int y = 0; y <= TextureAtlas.Height - texture.Height; y++) + private void ArrangeAtlasesInVRAM() { - for (int x = 0; x <= atlas.Width - texture.QuantizedWidth; x++) + foreach (var bitDepth in new[] { PSXBPP.TEX_16BIT, PSXBPP.TEX_8BIT, PSXBPP.TEX_4BIT }) { - var candidateRect = new Rect(x, y, texture.QuantizedWidth, texture.Height); - if (!atlas.ContainedTextures.Any(tex => new Rect(tex.PackingX, tex.PackingY, tex.QuantizedWidth, tex.Height).Overlaps(candidateRect))) + foreach (var atlas in _textureAtlases.Where(a => a.BitDepth == bitDepth)) { - texture.PackingX = x; - texture.PackingY = y; - atlas.ContainedTextures.Add(texture); - return true; - } - } - } - return false; - } - - private void ArrangeAtlasesInVRAM() - { - foreach (var bitDepth in new[] { PSXBPP.TEX_16BIT, PSXBPP.TEX_8BIT, PSXBPP.TEX_4BIT }) - { - foreach (var atlas in _textureAtlases.Where(a => a.BitDepth == bitDepth)) - { - bool placed = false; - for (int y = VRAM_HEIGHT - TextureAtlas.Height; y >= 0; y -= 256) - { - for (int x = 0; x <= VRAM_WIDTH - atlas.Width; x += 64) + bool placed = false; + for (int y = 0; y <= VRAM_HEIGHT - TextureAtlas.Height; y += 256) { - if (atlas.PositionX == 0 && atlas.PositionY == 0) + for (int x = 0; x <= VRAM_WIDTH - atlas.Width; x += 64) { - var candidateRect = new Rect(x, y, atlas.Width, TextureAtlas.Height); - if (IsPlacementValid(candidateRect)) + if (atlas.PositionX == 0 && atlas.PositionY == 0) { - atlas.PositionX = x; - atlas.PositionY = y; - _finalizedAtlases.Add(atlas); - placed = true; - break; + var candidateRect = new Rect(x, y, atlas.Width, TextureAtlas.Height); + if (IsPlacementValid(candidateRect)) + { + atlas.PositionX = x; + atlas.PositionY = y; + _finalizedAtlases.Add(atlas); + placed = true; + Debug.Log($"Placed an atlas at: {x},{y}"); + break; + } } } - } - if (placed) - { - foreach (PSXTexture2D texture in atlas.ContainedTextures) + if (placed) { - texture.TexpageNum = CalculateTexpage(atlas.PositionX, atlas.PositionY); + foreach (PSXTexture2D texture in atlas.ContainedTextures) + { + texture.TexpageNum = CalculateTexpage(atlas.PositionX, atlas.PositionY); + } + break; } - break; + } + if (!placed) + { + Debug.LogError($"Atlas with BitDepth {atlas.BitDepth} and Width {atlas.Width} could not be placed in VRAM."); } } + } + } + + private void AllocateCLUTs() + { + foreach (var texture in _finalizedAtlases.SelectMany(atlas => atlas.ContainedTextures)) + { + if (texture.ColorPalette == null || texture.ColorPalette.Count == 0) + continue; + + int clutWidth = texture.ColorPalette.Count; + int clutHeight = 1; + bool placed = false; + + for (int x = 0; x < VRAM_WIDTH; x++) + { + for (int y = 0; y <= VRAM_HEIGHT; y++) + { + var candidate = new Rect(x, y, clutWidth, clutHeight); + if (IsPlacementValid(candidate)) + { + _allocatedCLUTs.Add(candidate); + texture.ClutPackingX = x; + texture.ClutPackingY = y; + placed = true; + break; + } + } + if (placed) break; + } + if (!placed) { - Debug.LogError($"Atlas with BitDepth {atlas.BitDepth} and Width {atlas.Width} could not be placed in VRAM."); + Debug.LogError($"Failed to allocate CLUT for texture at {texture.PackingX}, {texture.PackingY}"); } } } - } - private void AllocateCLUTs() - { - foreach (var texture in _finalizedAtlases.SelectMany(atlas => atlas.ContainedTextures)) + private void BuildVram() { - if (texture.ColorPalette == null || texture.ColorPalette.Count == 0) - continue; - - int clutWidth = texture.ColorPalette.Count; - int clutHeight = 1; - bool placed = false; - - for (int y = 0; y < VRAM_HEIGHT; y++) + foreach (TextureAtlas atlas in _finalizedAtlases) { - for (int x = 0; x <= VRAM_WIDTH - clutWidth; x++) + foreach (PSXTexture2D texture in atlas.ContainedTextures) { - var candidate = new Rect(x, y, clutWidth, clutHeight); - if (IsPlacementValid(candidate)) + + for (int y = 0; y < texture.Height; y++) { - _allocatedCLUTs.Add(candidate); - texture.ClutPackingX = x; - texture.ClutPackingY = y; - placed = true; - break; + for (int x = 0; x < texture.QuantizedWidth; x++) + { + _vramPixels[x + atlas.PositionX + texture.PackingX, y + atlas.PositionY + texture.PackingY] = texture.ImageData[x, y]; + } + } + + if (texture.BitDepth != PSXBPP.TEX_16BIT) + { + for (int x = 0; x < texture.ColorPalette.Count; x++) + { + _vramPixels[x + texture.ClutPackingX, texture.ClutPackingY] = texture.ColorPalette[x]; + } } } - if (placed) break; - } - - if (!placed) - { - Debug.LogError($"Failed to allocate CLUT for texture at {texture.PackingX}, {texture.PackingY}"); } } - } - private bool IsPlacementValid(Rect rect) - { - Rect adjustedRect = new Rect(rect.x, VRAM_HEIGHT - rect.y - rect.height, rect.width, rect.height); - return !_finalizedAtlases.Any(a => AtlasOverlaps(a, rect)) && !_reservedAreas.Any(b => b.Overlaps(adjustedRect)); - } + private bool IsPlacementValid(Rect rect) + { - private bool AtlasOverlaps(TextureAtlas atlas, Rect rect) - { - Rect atlasRect = new Rect(atlas.PositionX, atlas.PositionY, atlas.Width, TextureAtlas.Height); - return atlasRect.Overlaps(rect); - } + if (rect.x + rect.width > VRAM_WIDTH) return false; + if (rect.y + rect.height > VRAM_HEIGHT) return false; - private int CalculateTexpage(int x, int y) - { - int columns = 16; - int rows = 2; - int colIndex = x / 64; - int rowIndex = (rows - 1) - (y / 256); - return (rowIndex * columns) + colIndex; + bool overlapsAtlas = _finalizedAtlases.Any(a => new Rect(a.PositionX, a.PositionY, a.Width, TextureAtlas.Height).Overlaps(rect)); + bool overlapsReserved = _reservedAreas.Any(r => r.Overlaps(rect)); + bool overlapsCLUT = _allocatedCLUTs.Any(c => c.Overlaps(rect)); + + return !(overlapsAtlas || overlapsReserved || overlapsCLUT); + } + + private int CalculateTexpage(int x, int y) + { + int columns = 16; + int colIndex = x / 64; + int rowIndex = y / 256; + return (rowIndex * columns) + colIndex; + } } -} +} \ No newline at end of file