diff --git a/Sandbox/content/Textures/Dirt.png b/Sandbox/content/Textures/Dirt.png new file mode 100644 index 0000000..db0c9ae Binary files /dev/null and b/Sandbox/content/Textures/Dirt.png differ diff --git a/Sandbox/content/Textures/GrassSide.png b/Sandbox/content/Textures/GrassSide.png new file mode 100644 index 0000000..c2c31c4 Binary files /dev/null and b/Sandbox/content/Textures/GrassSide.png differ diff --git a/Sandbox/content/Textures/GrassTop.png b/Sandbox/content/Textures/GrassTop.png new file mode 100644 index 0000000..53509c7 Binary files /dev/null and b/Sandbox/content/Textures/GrassTop.png differ diff --git a/Sandbox/content/Textures/Stone.png b/Sandbox/content/Textures/Stone.png new file mode 100644 index 0000000..999fd97 Binary files /dev/null and b/Sandbox/content/Textures/Stone.png differ diff --git a/Sandbox/src/VoxelFun/BlockModel.bf b/Sandbox/src/VoxelFun/BlockModel.bf index 4920205..6714da5 100644 --- a/Sandbox/src/VoxelFun/BlockModel.bf +++ b/Sandbox/src/VoxelFun/BlockModel.bf @@ -27,69 +27,116 @@ namespace Sandbox.VoxelFun { Color _color; + BlockTexture _textureTop; + BlockTexture _textureSide; + BlockTexture _textureBottom; + + public this(Color color) { _color = color; + + bottomCoords = sideCoords = topCoords = (.Zero, .UnitX, .UnitY, .One); + } + + public this(Color color, BlockTexture texture) : this(color, texture, texture, texture) + { + } + + public this(Color color, BlockTexture topTexture, BlockTexture sideTexture, BlockTexture bottomTexture) + { + _color = color; + _textureTop = topTexture; + _textureSide = sideTexture; + _textureBottom = bottomTexture; + + CalculateTexCoords(); + } + + typealias TexCoords = (Vector2 Zero, Vector2 UnitX, Vector2 UnitY, Vector2 One); + + TexCoords topCoords; + TexCoords sideCoords; + TexCoords bottomCoords; + + private void CalculateTexCoords() + { + topCoords = GetQuadCoords(_textureTop); + sideCoords = GetQuadCoords(_textureSide); + bottomCoords = GetQuadCoords(_textureBottom); + } + + private TexCoords GetQuadCoords(BlockTexture texture) + { + TexCoords coords; + + coords.UnitX = texture.TransformTexCoords(.UnitX); + coords.UnitY = texture.TransformTexCoords(.UnitY); + coords.Zero = texture.TransformTexCoords(.Zero); + coords.One = texture.TransformTexCoords(.One); + + return coords; } public override void GenerateGeometry(Block block, Vector3 blockPosition, BlockFace visibleFaces, List vertices, List indices, ref uint32 lastIndex) { + if(visibleFaces.HasFlag(.Back)) { - vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, .UnitY)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, .One)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, .UnitX)); - vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, .Zero)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, sideCoords.UnitY)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, sideCoords.One)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, sideCoords.UnitX)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, sideCoords.Zero)); AddQuadIndices(ref lastIndex, indices); } if(visibleFaces.HasFlag(.Top)) { - vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, .UnitY)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, .One)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, .UnitX)); - vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, .Zero)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, topCoords.UnitY)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, topCoords.One)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, topCoords.UnitX)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, topCoords.Zero)); AddQuadIndices(ref lastIndex, indices); } if(visibleFaces.HasFlag(.Bottom)) { - vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, .UnitY)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, .One)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, .UnitX)); - vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, .Zero)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, bottomCoords.UnitY)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, bottomCoords.One)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, bottomCoords.UnitX)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, bottomCoords.Zero)); AddQuadIndices(ref lastIndex, indices); } if(visibleFaces.HasFlag(.Front)) { - vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, .UnitY)); - vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, .One)); - vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, .UnitX)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, .Zero)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, sideCoords.UnitY)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, sideCoords.One)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, sideCoords.UnitX)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, sideCoords.Zero)); AddQuadIndices(ref lastIndex, indices); } if(visibleFaces.HasFlag(.Right)) { - vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, .UnitY)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, .One)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, .UnitX)); - vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, .Zero)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, sideCoords.UnitY)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, sideCoords.One)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, sideCoords.UnitX)); + vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, sideCoords.Zero)); AddQuadIndices(ref lastIndex, indices); } if(visibleFaces.HasFlag(.Left)) { - vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, .UnitY)); - vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, .One)); - vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, .UnitX)); - vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, .Zero)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, sideCoords.UnitY)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, sideCoords.One)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, sideCoords.UnitX)); + vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, sideCoords.Zero)); AddQuadIndices(ref lastIndex, indices); } diff --git a/Sandbox/src/VoxelFun/BlockTexture.bf b/Sandbox/src/VoxelFun/BlockTexture.bf new file mode 100644 index 0000000..d47b347 --- /dev/null +++ b/Sandbox/src/VoxelFun/BlockTexture.bf @@ -0,0 +1,28 @@ +using System; +using GlitchyEngine.Math; + +namespace Sandbox.VoxelFun +{ + class BlockTexture + { + private String _fileName; + + public String FileName => _fileName; + + private Vector2 _atlasStart; + private Vector2 _atlasSize; + + [AllowAppend] + public this(String fileName) + { + String str = append String(fileName); + + _fileName = str; + } + + public Vector2 TransformTexCoords(Vector2 texCoords) + { + return _atlasStart + texCoords * _atlasSize; + } + } +} diff --git a/Sandbox/src/VoxelFun/BlockTextures.bf b/Sandbox/src/VoxelFun/BlockTextures.bf new file mode 100644 index 0000000..8310edd --- /dev/null +++ b/Sandbox/src/VoxelFun/BlockTextures.bf @@ -0,0 +1,163 @@ +using System.Collections; +using GlitchyEngine.Renderer; +using System.Collections; +using GlitchyEngine.Renderer; +using GlitchyEngine.Math; +using GlitchyEngine; +using DirectX.D3D11; +using System; +namespace Sandbox.VoxelFun +{ + class BlockTextures + { + static List _textures = new List() ~ DeleteContainerAndItems!(_); + + static TextureAtlas _atlas ~ _?.ReleaseRef(); + + public static BlockTexture Stone; + public static BlockTexture Dirt; + public static BlockTexture GrassTop; + public static BlockTexture GrassSide; + + public static TextureAtlas Atlas => _atlas; + + public static void Init(GraphicsContext context) + { + Stone = RegisterTexture(.. new BlockTexture("Content\\Textures\\Stone.png")); + Dirt = RegisterTexture(.. new BlockTexture("Content\\Textures\\Dirt.png")); + GrassTop = RegisterTexture(.. new BlockTexture("Content\\Textures\\GrassTop.png")); + GrassSide = RegisterTexture(.. new BlockTexture("Content\\Textures\\GrassSide.png")); + + GenerateAtlas(context); + } + + private static void RegisterTexture(BlockTexture texture) + { + _textures.Add(texture); + } + + static void GenerateAtlas(GraphicsContext context) + { + _atlas = new TextureAtlas(context, _textures); + + GlitchyEngine.Renderer.SamplerStateDescription desc = .(); + desc.MagFilter = .Point; + desc.MinFilter = .Point; + desc.MipFilter = .Point; + desc.MipMaxLOD = 0; + desc.MipMinLOD = 0; + desc.MaxAnisotropy = 0; + + _atlas.SamplerState = new SamplerState(context, desc)..ReleaseRefNoDelete(); + } + } + + class TextureAtlas : Texture + { + // TODO: native code + private ID3D11Texture2D* nativeTexture ~ _?.Release(); + + public override extern uint32 Width {get;} + public override extern uint32 Height {get;} + public override uint32 Depth => 1; + public override extern uint32 ArraySize {get;} + public override extern uint32 MipLevels {get;} + + public this(GraphicsContext context, List textures) : base(context) + { + // Todo: rewrite this garbage algorithm + + List<(Color* data, uint32 width, uint32 height, BlockTexture blockTex)> textureDatas = new .(textures.Count); + defer + { + for(let item in textureDatas) + { + LodePng.LodePng.Free(item.data); + } + + delete textureDatas; + } + + uint32 maxWidth = 0; + uint32 maxHeight = 0; + + for(let texture in textures) + { + (Color* data, uint32 width, uint32 height, BlockTexture blockTex) tex; + tex.blockTex = texture; + + let error = LodePng.LodePng.Decode32File(out tex.data, out tex.width, out tex.height, texture.FileName); + + if(error != 0) + Log.ClientLogger.Error($"Failed to load texture \"{texture.FileName}\". ({error}) \"{StringView(LodePng.LodePng.ErrorText(error))}\""); + + if(tex.width > maxWidth) + maxWidth = tex.width; + + if(tex.height > maxHeight) + maxHeight = tex.height; + + textureDatas.Add(tex); + } + + uint32 texturesX = (.)System.Math.Ceiling(System.Math.Sqrt(textureDatas.Count)); + uint32 texturesY = (.)System.Math.Ceiling((float)textureDatas.Count / (float)texturesX); + + uint32 textureWidth = texturesX * maxWidth; + uint32 textureHeight = texturesY * maxHeight; + + Color[] atlasColors = new DirectX.Color[textureWidth * textureHeight]; + defer delete atlasColors; + + uint32 currentX = 0; + uint32 currentY = 0; + + for(let texture in textureDatas) + { + texture.blockTex.[Friend]_atlasStart = .((float)currentX / (float)textureWidth, (float)currentY / (float)textureHeight); + texture.blockTex.[Friend]_atlasSize = .((float)texture.width / (float)textureWidth, (float)texture.height / (float)textureHeight); + + for(uint32 penY = currentY, uint32 y = 0; y < texture.height; penY++, y++) + for(uint32 penX = currentX, uint32 x = 0; x < texture.width; penX++, x++) + { + atlasColors[penX + penY * textureWidth] = texture.data[x + y * texture.width]; + } + + currentX += maxWidth; + if(currentX >= textureWidth) + { + currentX = 0; + currentY += maxHeight; + } + } + + Texture2DDescription desc; + desc.Width = textureWidth; + desc.Height = textureHeight; + desc.ArraySize = 1; + desc.MipLevels = 1; + desc.SampleDesc = .(1, 0); + desc.Usage = .Immutable; + desc.MiscFlags = .None; + desc.BindFlags = .ShaderResource; + desc.CpuAccessFlags = .None; + desc.Format = .R8G8B8A8_UNorm; + + //SubresourceData data = .(atlasColors.CArray(), textureWidth, textureWidth * textureHeight); + SubresourceData data = .(atlasColors.CArray(), textureWidth * sizeof(Color), 0); + + // TODO: this is bad platform dependant code... + var result = _context.[Friend]nativeDevice.CreateTexture2D(ref desc, &data, &nativeTexture); + + //LodePng.LodePng.Encode32File("testout.png", textureD) + + if(result.Failed) + Log.ClientLogger.Error($"Failed to create atlas texture. Error ({result.Underlying}): {result}"); + + result = _context.[Friend]nativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeView); + + if(result.Failed) + Log.ClientLogger.Error($"Failed to create atlas texture. Error ({result.Underlying}): {result}"); + } + } +} diff --git a/Sandbox/src/VoxelFun/ChunkManager.bf b/Sandbox/src/VoxelFun/ChunkManager.bf index f606e2b..a32ac18 100644 --- a/Sandbox/src/VoxelFun/ChunkManager.bf +++ b/Sandbox/src/VoxelFun/ChunkManager.bf @@ -84,7 +84,6 @@ namespace Sandbox.VoxelFun private HashSet _generatingChunks = new .() ~ delete _; private Monitor _generatingChunksLock = new .() ~ delete _; - public Texture2D Texture ~ _?.ReleaseRef(); public Effect TextureEffect ~ _?.ReleaseRef(); VoxelGeometryGenerator voxelGeoGen = new VoxelGeometryGenerator() ~ delete _; @@ -618,7 +617,7 @@ namespace Sandbox.VoxelFun chunkGeo.AddRef(); - Texture.Bind(); + BlockTextures.Atlas.Bind(); Renderer.Submit(chunk.Geometry, TextureEffect, chunk.Transform); chunkGeo.ReleaseRef(); diff --git a/Sandbox/src/VoxelFun/Models.bf b/Sandbox/src/VoxelFun/Models.bf index b9a36f8..dd5febf 100644 --- a/Sandbox/src/VoxelFun/Models.bf +++ b/Sandbox/src/VoxelFun/Models.bf @@ -13,9 +13,9 @@ namespace Sandbox.VoxelFun public static void Init() { - Stone = RegisterModel(Blocks.Stone, .. new BlockModel(.Gray)); - Grass = RegisterModel(Blocks.Grass, .. new BlockModel(.Green)); - Dirt = RegisterModel(Blocks.Dirt, .. new BlockModel(.Brown)); + Stone = RegisterModel(Blocks.Stone, .. new BlockModel(.White, BlockTextures.Stone)); + Grass = RegisterModel(Blocks.Grass, .. new BlockModel(.White, BlockTextures.GrassTop, BlockTextures.GrassSide, BlockTextures.Dirt)); + Dirt = RegisterModel(Blocks.Dirt, .. new BlockModel(.White, BlockTextures.Dirt)); } public static void RegisterModel(Block block, Model model) diff --git a/Sandbox/src/VoxelFun/VoxelTestLayer.bf b/Sandbox/src/VoxelFun/VoxelTestLayer.bf index fedff46..e230d6e 100644 --- a/Sandbox/src/VoxelFun/VoxelTestLayer.bf +++ b/Sandbox/src/VoxelFun/VoxelTestLayer.bf @@ -88,9 +88,11 @@ namespace Sandbox.VoxelFun public this() : base("VoxelTest") { Blocks.Init(); - Models.Init(); _context = Application.Get().Window.Context..AddRef(); + + BlockTextures.Init(_context); + Models.Init(); _effectLibrary = new EffectLibrary(_context); @@ -255,7 +257,6 @@ namespace Sandbox.VoxelFun } _world.ChunkManager = new ChunkManager(_context, _vertexLayout, _world); - _world.ChunkManager.Texture = _texture..AddRef(); _world.ChunkManager.TextureEffect = _textureEffect..AddRef(); }