Compare commits
3 Commits
49d35cab5b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e14786668 | ||
|
|
ab8816e27c | ||
|
|
c6916a6dd5 |
BIN
Assets/textures/VoxelShadeAtlas.png
Normal file
BIN
Assets/textures/VoxelShadeAtlas.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 910 KiB |
@@ -34,7 +34,7 @@ public sealed class Game : GameWindow
|
||||
GL.Enable(EnableCap.CullFace);
|
||||
|
||||
this._shader = new Shader("Assets/shaders/chunk.vert", "Assets/shaders/chunk.frag");
|
||||
this._texture = new Texture("Assets/textures/atlas.png");
|
||||
this._texture = new Texture("Assets/textures/VoxelShadeAtlas.png");
|
||||
|
||||
this._world = new World.World(this._registry);
|
||||
|
||||
|
||||
@@ -38,9 +38,9 @@ be unit-tested headless.
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] Baseline: window, fly-camera, one face-culled textured chunk
|
||||
- [x] Baseline: window, fly-camera, one face-culled textured chunk
|
||||
- [ ] Procedural terrain (noise)
|
||||
- [ ] Multiple chunks + frustum culling
|
||||
- [x] Multiple chunks + frustum culling
|
||||
- [ ] Player controller + AABB collision
|
||||
- [ ] Block break / place
|
||||
- [ ] Lighting
|
||||
|
||||
@@ -34,9 +34,9 @@ public class BlockRegistryTests
|
||||
var registry = new BlockRegistry();
|
||||
IBlock grass = registry.Get(3);
|
||||
|
||||
Assert.Equal(0, grass.GetTextureIndex(BlockFace.Top));
|
||||
Assert.Equal(1, grass.GetTextureIndex(BlockFace.Bottom));
|
||||
Assert.Equal(3, grass.GetTextureIndex(BlockFace.North));
|
||||
Assert.Equal(new TileCoord(0, 1), grass.GetTextureTile(BlockFace.Top));
|
||||
Assert.Equal(new TileCoord(0, 0), grass.GetTextureTile(BlockFace.Bottom));
|
||||
Assert.Equal(new TileCoord(1, 1), grass.GetTextureTile(BlockFace.North));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK" Version="4.9.4" />
|
||||
<PackageReference Include="SimplexNoise" Version="2.0.0" />
|
||||
<PackageReference Include="StbImageSharp" Version="2.30.15" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -19,4 +20,8 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="World\WorldGen\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -6,5 +6,5 @@ public sealed class AirBlock : IBlock
|
||||
public string Name => "Air";
|
||||
public bool IsOpaque => false;
|
||||
public bool IsSolid => false;
|
||||
public int GetTextureIndex(BlockFace face) => 0;
|
||||
public TileCoord GetTextureTile(BlockFace face) => new(0, 0);
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@ public sealed class DirtBlock : IBlock
|
||||
public string Name => "Dirt";
|
||||
public bool IsOpaque => true;
|
||||
public bool IsSolid => true;
|
||||
public int GetTextureIndex(BlockFace face) => 1;
|
||||
public TileCoord GetTextureTile(BlockFace face) => new(0, 0);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ public sealed class GrassBlock : IBlock
|
||||
public bool IsOpaque => true;
|
||||
public bool IsSolid => true;
|
||||
|
||||
public int GetTextureIndex(BlockFace face) => face switch
|
||||
public TileCoord GetTextureTile(BlockFace face) => face switch
|
||||
{
|
||||
BlockFace.Top => 0, // grass top
|
||||
BlockFace.Bottom => 1, // dirt
|
||||
_ => 3 // grass side
|
||||
BlockFace.Top => new(0, 1), // grass top
|
||||
BlockFace.Bottom => new(0, 0), // dirt
|
||||
_ => new(1, 1) // grass side
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ public interface IBlock
|
||||
// Reserved for future collision (not used in the baseline).
|
||||
bool IsSolid { get; }
|
||||
|
||||
// Atlas tile index for the given face.
|
||||
int GetTextureIndex(BlockFace face);
|
||||
// Atlas tile coordinate for the given face.
|
||||
TileCoord GetTextureTile(BlockFace face);
|
||||
|
||||
// Interaction hooks — capability present now, system built post-baseline.
|
||||
void OnInteract() { }
|
||||
|
||||
@@ -6,5 +6,5 @@ public sealed class StoneBlock : IBlock
|
||||
public string Name => "Stone";
|
||||
public bool IsOpaque => true;
|
||||
public bool IsSolid => true;
|
||||
public int GetTextureIndex(BlockFace face) => 2;
|
||||
public TileCoord GetTextureTile(BlockFace face) => new(1, 0);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ namespace VoxelEngine.World.Chunk;
|
||||
|
||||
public sealed class ChunkMesher : IChunkMesher
|
||||
{
|
||||
private const int AtlasTiles = 4; // horizontal strip of 4 tiles
|
||||
private const int TileSizePixels = 32; // each tile is 32x32 pixels
|
||||
private const int AtlasSizePixels = 64; // atlas is 64x64 pixels (2x2 grid)
|
||||
|
||||
private const float TileSize = (float)TileSizePixels / AtlasSizePixels; // normalized tile size
|
||||
|
||||
// Per-face geometry: base corner + two in-plane edge vectors (u, v) with
|
||||
// cross(u, v) == outward normal, guaranteeing CCW winding from outside.
|
||||
@@ -69,17 +72,18 @@ public sealed class ChunkMesher : IChunkMesher
|
||||
: (neighborOpaqueOutside?.Invoke(nx, ny, nz) ?? false);
|
||||
if (neighborOpaque) continue;
|
||||
|
||||
ChunkMesher.AddFace(verts, x, y, z, face, block.GetTextureIndex(face.Kind));
|
||||
ChunkMesher.AddFace(verts, x, y, z, face, block.GetTextureTile(face.Kind));
|
||||
}
|
||||
}
|
||||
|
||||
return verts.ToArray();
|
||||
}
|
||||
|
||||
private static void AddFace(List<float> verts, int x, int y, int z, Face face, int texIndex)
|
||||
private static void AddFace(List<float> verts, int x, int y, int z, Face face, TileCoord tile)
|
||||
{
|
||||
float tileWidth = 1f / ChunkMesher.AtlasTiles;
|
||||
float uBase = texIndex * tileWidth;
|
||||
float uBase = tile.X * ChunkMesher.TileSize;
|
||||
// Image is flipped on load, so offset from the top to keep tile (0,0) = top-left.
|
||||
float vBase = 1f - (tile.Y + 1) * ChunkMesher.TileSize;
|
||||
|
||||
foreach (int corner in ChunkMesher.Triangle)
|
||||
{
|
||||
@@ -89,10 +93,11 @@ public sealed class ChunkMesher : IChunkMesher
|
||||
float py = y + face.Base.y + face.U.y * cu + face.V.y * cv;
|
||||
float pz = z + face.Base.z + face.U.z * cu + face.V.z * cv;
|
||||
|
||||
float u = uBase + cu * tileWidth;
|
||||
float u = uBase + cu * ChunkMesher.TileSize;
|
||||
float v = vBase + cv * ChunkMesher.TileSize;
|
||||
|
||||
verts.Add(px); verts.Add(py); verts.Add(pz);
|
||||
verts.Add(u); verts.Add(cv);
|
||||
verts.Add(u); verts.Add(v);
|
||||
verts.Add(face.Shade);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user