112 lines
3.2 KiB
C#
112 lines
3.2 KiB
C#
|
|
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;
|
||
|
|
}
|
||
|
|
}
|