Skip to content
This repository has been archived by the owner on Nov 25, 2024. It is now read-only.

Commit

Permalink
-Add rolling array to chunk render slots
Browse files Browse the repository at this point in the history
  • Loading branch information
Cassunshine committed Nov 30, 2023
1 parent c9f238b commit 1de0f2c
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 80 deletions.
85 changes: 53 additions & 32 deletions Client/Rendering/World/ChunkRenderSlot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,70 +13,81 @@ namespace Voxel.Client.Rendering.World;
/// <summary>
/// Holds the mesh data for a single chunk, its render state, etc, etc.
/// </summary>
public class ChunkRenderSlot : Renderer {

public readonly ivec3 RelativePosition;
public class ChunkRenderSlot : Renderer
{
private readonly object MeshLock = new();

public uint? lastVersion;
public Chunk? targetChunk { get; private set; }
private ivec3 realPosition;

public ivec3 RealPosition { get; private set; }
private ChunkMesh? mesh;

public ChunkRenderSlot(VoxelClient client, ivec3 relativePosition) : base(client) {
RelativePosition = relativePosition;
public ChunkRenderSlot(VoxelClient client) : base(client)
{
}

public override void Render(double delta) {

public override void Render(double delta)
{
//Do nothing if this chunk render slot doesn't have a chunk yet, or if the chunk it does have is empty.
if (targetChunk == null || targetChunk.IsEmpty)
return;

//Console.Out.Write(lastVersion);

if (lastVersion != targetChunk.GetVersion())
Rebuild();

//Store this to prevent race conditions between == null and .render
lock (MeshLock) {
lock (MeshLock)
{
if (mesh == null)
return;
mesh.Render();
}
}

public void Move(ivec3 newCenterPos, LoadedChunkSection chunks) {
realPosition = newCenterPos + RelativePosition;
public void Move(ivec3 absolutePos, LoadedChunkSection chunks)
{
if (RealPosition == absolutePos)
return;

RealPosition = absolutePos;
//Should never be null bc this only has 1 callsite that already null checks it
targetChunk = chunks.GetChunkRelative(RelativePosition);
targetChunk = chunks.GetChunkAbsolute(RealPosition);
lastVersion = null;

if (targetChunk == null || targetChunk.ChunkPosition != absolutePos)
throw new InvalidOperationException("");
}


private void Rebuild() {
if (!ChunkMeshBuilder.Rebuild(this, realPosition))
private void Rebuild()
{
if (!ChunkMeshBuilder.Rebuild(this, RealPosition))
return;

//Console.Out.WriteLine("Rebuild");

lastVersion = targetChunk!.GetVersion();
}

public void SetMesh(ChunkMesh mesh) {
lock (MeshLock) {
public void SetMesh(ChunkMesh mesh)
{
lock (MeshLock)
{
this.mesh?.Dispose(); //Dispose of old, if it exists.
this.mesh = mesh; //Slot in new.
}
}

public override void Dispose() {
public override void Dispose()
{
mesh?.Dispose();
}

public class ChunkMesh : IDisposable {
public class ChunkMesh : IDisposable
{
public readonly VoxelClient Client;
public readonly RenderSystem RenderSystem;

Expand All @@ -88,11 +99,13 @@ public class ChunkMesh : IDisposable {
private readonly TypedDeviceBuffer<ChunkMeshUniform> UniformBuffer;
private readonly ResourceSet UniformResourceSet;

public ChunkMesh(VoxelClient client, Span<BasicVertex.Packed> packedVertices, uint indexCount, ivec3 position) {
public ChunkMesh(VoxelClient client, Span<BasicVertex.Packed> packedVertices, uint indexCount, ivec3 position)
{
Client = client;
RenderSystem = Client.RenderSystem;

Buffer = RenderSystem.ResourceFactory.CreateBuffer(new() {
Buffer = RenderSystem.ResourceFactory.CreateBuffer(new()
{
SizeInBytes = (uint)Marshal.SizeOf<BasicVertex.Packed>() * (uint)packedVertices.Length,
Usage = BufferUsage.VertexBuffer
});
Expand All @@ -102,24 +115,29 @@ public ChunkMesh(VoxelClient client, Span<BasicVertex.Packed> packedVertices, ui
Position = position;
WorldPosition = position.ChunkToWorldPosition();

UniformBuffer = new(new() {
UniformBuffer = new(new()
{
Usage = BufferUsage.Dynamic | BufferUsage.UniformBuffer
}, RenderSystem);
UniformResourceSet = RenderSystem.ResourceFactory.CreateResourceSet(new() {
UniformResourceSet = RenderSystem.ResourceFactory.CreateResourceSet(new()
{
Layout = Client.GameRenderer.WorldRenderer.ChunkRenderer.ChunkResourceLayout,
BoundResources = new BindableResource[] {
BoundResources = new BindableResource[]
{
UniformBuffer.BackingBuffer
}
});
}

public void Render() {
public void Render()
{
//Just in case...
if (Buffer == null)
return;

//Set up chunk transform relative to camera.
UniformBuffer.SetValue(new ChunkMeshUniform {
UniformBuffer.SetValue(new ChunkMeshUniform
{
modelMatrix = mat4.Translate((vec3)(WorldPosition - CameraStateManager.currentCameraPosition)).Transposed
});
RenderSystem.MainCommandList.SetGraphicsResourceSet(2, UniformResourceSet);
Expand All @@ -128,16 +146,19 @@ public void Render() {
RenderSystem.MainCommandList.DrawIndexed(IndexCount);
}

public void Dispose() {
public void Dispose()
{
RenderSystem.GraphicsDevice.DisposeWhenIdle(Buffer);
}


private struct ChunkMeshUniform {
private struct ChunkMeshUniform
{
public mat4 modelMatrix = mat4.Identity.Transposed;


public ChunkMeshUniform() {
public ChunkMeshUniform()
{
}
}
}
Expand Down
96 changes: 63 additions & 33 deletions Client/Rendering/World/ChunkRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

namespace Voxel.Client.Rendering.World;

public class ChunkRenderer : Renderer {

public class ChunkRenderer : Renderer
{
public readonly Pipeline ChunkPipeline;
public readonly ResourceLayout ChunkResourceLayout;

Expand All @@ -24,15 +24,18 @@ public class ChunkRenderer : Renderer {

private ivec3 renderPosition = ivec3.Zero;

private ChunkRenderSlot? this[int x, int y, int z] {
get {
private ChunkRenderSlot? this[int x, int y, int z]
{
get
{
if (renderSlots == null) return null;

var index = z + y * realRenderDistance + x * realRenderDistance * realRenderDistance;

return renderSlots[index];
}
set {
set
{
if (renderSlots == null) return;

var index = z + y * realRenderDistance + x * realRenderDistance * realRenderDistance;
Expand All @@ -41,15 +44,17 @@ public class ChunkRenderer : Renderer {
}
}

private ChunkRenderSlot? this[ivec3 pos] {
private ChunkRenderSlot? this[ivec3 pos]
{
get => this[pos.x, pos.y, pos.z];
set => this[pos.x, pos.y, pos.z] = value;
}

public ChunkRenderer(VoxelClient client) : base(client) {
public ChunkRenderer(VoxelClient client) : base(client)
{
chunks = new(client.world!, renderPosition, ClientConfig.General.renderDistance, ClientConfig.General.renderDistance);
SetRenderDistance(ClientConfig.General.renderDistance);

TerrainAtlas = new("main", client.RenderSystem);
AtlasLoader.LoadAtlas(RenderSystem.Game.AssetReader, TerrainAtlas, RenderSystem);
BlockModelManager.Init(RenderSystem.Game.AssetReader, TerrainAtlas);
Expand All @@ -62,45 +67,53 @@ public ChunkRenderer(VoxelClient client) : base(client) {
if (!client.RenderSystem.ShaderManager.GetShaders("shaders/simple", out var shaders))
throw new("Shaders not present.");

ChunkPipeline = ResourceFactory.CreateGraphicsPipeline(new() {
ChunkPipeline = ResourceFactory.CreateGraphicsPipeline(new()
{
BlendState = BlendStateDescription.SingleOverrideBlend,
DepthStencilState = new() {
DepthStencilState = new()
{
DepthComparison = ComparisonKind.LessEqual,
DepthTestEnabled = true,
DepthWriteEnabled = true,
},
Outputs = RenderSystem.GraphicsDevice.SwapchainFramebuffer.OutputDescription,
PrimitiveTopology = PrimitiveTopology.TriangleList,
RasterizerState = new() {
RasterizerState = new()
{
CullMode = FaceCullMode.Back,
DepthClipEnabled = true,
FillMode = PolygonFillMode.Solid,
FrontFace = FrontFace.CounterClockwise,
ScissorTestEnabled = false
},
ResourceLayouts = new[] {
ResourceLayouts = new[]
{
Client.GameRenderer.CameraStateManager.CameraResourceLayout,
RenderSystem.TextureManager.TextureResourceLayout,
ChunkResourceLayout
},
ShaderSet = new() {
VertexLayouts = new[] {
ShaderSet = new()
{
VertexLayouts = new[]
{
BasicVertex.Packed.Layout
},
Shaders = shaders
}
});
}

public void Reload() {

public void Reload()
{
if (renderSlots == null)
return;

foreach (var slot in renderSlots)
slot.lastVersion = null;
}

public override void Render(double delta) {
public override void Render(double delta)
{
if (renderSlots == null)
return;

Expand All @@ -112,11 +125,12 @@ public override void Render(double delta) {
CommandList.SetIndexBuffer(RenderSystem.CommonIndexBuffer, IndexFormat.UInt32);
foreach (var slot in renderSlots)
slot.Render(delta);

//Console.Out.WriteLine();
}

public void SetRenderDistance(int distance) {
public void SetRenderDistance(int distance)
{
chunks.Resize(distance, distance);
if (renderSlots != null)
foreach (var slot in renderSlots)
Expand All @@ -129,35 +143,51 @@ public void SetRenderDistance(int distance) {

for (int x = 0; x < realRenderDistance; x++)
for (int y = 0; y < realRenderDistance; y++)
for (int z = 0; z < realRenderDistance; z++) {
var slot = new ChunkRenderSlot(Client, new ivec3(x, y, z) - distance);
slot.Move(renderPosition, chunks);

this[x, y, z] = slot;
for (int z = 0; z < realRenderDistance; z++)
{
var absolutePos = (new ivec3(x, y, z) - renderDistance) + renderPosition;
var slot = new ChunkRenderSlot(Client);
renderSlots[GetLoopedArrayIndex(absolutePos)] = slot;
slot.Move(absolutePos, chunks);
}

//Sort by distance so that closer chunks are rebuilt first.
Array.Sort(renderSlots, (a, b) => a.RelativePosition.LengthSqr.CompareTo(b.RelativePosition.LengthSqr));
Array.Sort(renderSlots, (a, b) => (a.RealPosition - renderPosition).LengthSqr.CompareTo((b.RealPosition - renderPosition).LengthSqr));
}

public void SetRenderPosition(dvec3 worldPosition) {
public void SetRenderPosition(dvec3 worldPosition)
{
var newPos = worldPosition.WorldToChunkPosition();

if (newPos == renderPosition || renderSlots == null)
return;

renderPosition = newPos;

chunks.Move(newPos);

foreach (var slot in renderSlots)
slot.Move(renderPosition, chunks);

for (int x = 0; x < realRenderDistance; x++)
for (int y = 0; y < realRenderDistance; y++)
for (int z = 0; z < realRenderDistance; z++)
{
var absolutePos = (new ivec3(x, y, z) - renderDistance) + renderPosition;
var slot = renderSlots[GetLoopedArrayIndex(absolutePos)];
slot.Move(absolutePos, chunks);
}

//Sort by distance so that closer chunks are rebuilt first.
Array.Sort(renderSlots, (a, b) => a.RelativePosition.LengthSqr.CompareTo(b.RelativePosition.LengthSqr));
Array.Sort(renderSlots, (a, b) => (a.RealPosition - renderPosition).LengthSqr.CompareTo((b.RealPosition - renderPosition).LengthSqr));
}


private int GetLoopedArrayIndex(ivec3 pos)
{
pos = new ivec3(MathHelper.Repeat(pos.x, realRenderDistance), MathHelper.Repeat(pos.y, realRenderDistance), MathHelper.Repeat(pos.z, realRenderDistance));
return pos.z + pos.y * realRenderDistance + pos.x * realRenderDistance * realRenderDistance;
}

public override void Dispose() {
public override void Dispose()
{
if (renderSlots == null)
return;

Expand Down
2 changes: 1 addition & 1 deletion Client/VoxelClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public override void OnTick() {

camera.MoveAndSlide(world!, inputDir);

// GameRenderer.WorldRenderer.ChunkRenderer.SetRenderPosition(camera.position);
GameRenderer.WorldRenderer.ChunkRenderer.SetRenderPosition(camera.position);
}

public override void OnWindowResize() {
Expand Down
Loading

0 comments on commit 1de0f2c

Please sign in to comment.