using Godot; using SJK.Voxels; using System; public partial class UVWrapVisualizer : Control { public enum WrapperType { Normal, Twisted } [Export] public int WorldSize = 8; [Export] public int ChunkSize = 16; [Export] public int RenderChunks = 4; [Export] public WrapperType WrapMode = WrapperType.Twisted; private ImageTexture texture; public override void _Ready() { GenerateTexture(); } private IWorldWrapper wrapper = new EndlessSizeXZWorld(10); private void GenerateTexture() { int texSize = WorldSize * ChunkSize * RenderChunks; var image = Godot.Image.CreateEmpty(texSize, texSize, false, Image.Format.Rgb8); wrapper = WrapMode switch { WrapperType.Normal => new ToroidalWrapping(Vector3I.One * WorldSize), WrapperType.Twisted => new MirroredZWrappingXOffset(Vector3I.One * WorldSize), _ => throw new Exception(), }; GD.Print(wrapper.GetType()); for (int gz = 0; gz < texSize; gz++) { for (int gx = 0; gx < texSize; gx++) { Vector2I global = new Vector2I(gx - texSize / 2, gz - texSize / 2); // center origin var v3 = wrapper.Wrap(new Vector3I(global.X, 1, global.Y)); Vector2I local = new(v3.X, v3.Z); bool flip = false; // switch (WrapMode) // { // case WrapperType.Twisted: // (local, flip) = TwistedWrapInt(global, WorldSize, WorldSize); // break; // default: // local = new Vector2I(Mod(global.X, WorldSize), Mod(global.Y, WorldSize)); // flip = false; // break; // } // Encode local position into RGB (U and V channels) float u = local.X / (float)WorldSize; float v = local.Y / (float)WorldSize; Color color = new Color(u, v, flip ? 1.0f : 0.0f); image.SetPixel(gx, gz, color); } } texture = ImageTexture.CreateFromImage(image); QueueRedraw(); } public override void _Draw() { if (texture != null) { DrawTexture(texture, Vector2.Zero); } } // ========== Wrapping Logic ========== private (Vector2I local, bool flipped) TwistedWrapInt(Vector2I global, int W, int H) { int gx = global.X; int gy = global.Y; int yWrap = FloorDiv(gy, H); bool flip = (yWrap & 1) != 0; int tileY = gy % H; if (tileY < 0) tileY += H; int ly = flip ? (H - 1 - tileY) : tileY; int xOffset = flip ? W / 2 : 0; int wrappedX = gx + xOffset; int tileX = wrappedX % W; if (tileX < 0) tileX += W; int lx = flip ? (W - 1 - tileX) : tileX; return (new Vector2I(lx, ly), flip); } private int FloorDiv(int a, int b) { return (a >= 0) ? a / b : ((a + 1) / b) - 1; } private int Mod(int a, int b) { int r = a % b; return r < 0 ? r + b : r; } }