ClassiCube/ClassicalSharp/Rendering/MapRenderer.cs
UnknownShadow200 98d435b110 fix licensing
2017-01-20 09:12:04 +11:00

400 lines
No EOL
14 KiB
C#

// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
using System;
using ClassicalSharp.Entities;
using ClassicalSharp.Events;
using ClassicalSharp.Map;
using ClassicalSharp.GraphicsAPI;
using OpenTK;
namespace ClassicalSharp.Renderers {
public class ChunkInfo {
public ushort CentreX, CentreY, CentreZ;
public bool Visible = true, Empty = false;
public bool DrawLeft, DrawRight, DrawFront, DrawBack, DrawBottom, DrawTop;
#if OCCLUSION
public bool Visited = false, Occluded = false;
public byte OcclusionFlags, OccludedFlags, DistanceFlags;
#endif
public ChunkPartInfo[] NormalParts;
public ChunkPartInfo[] TranslucentParts;
public ChunkInfo(int x, int y, int z) {
CentreX = (ushort)(x + 8);
CentreY = (ushort)(y + 8);
CentreZ = (ushort)(z + 8);
}
public void Reset(int x, int y, int z) {
CentreX = (ushort)(x + 8);
CentreY = (ushort)(y + 8);
CentreZ = (ushort)(z + 8);
Visible = true; Empty = false;
DrawLeft = false; DrawRight = false; DrawFront = false;
DrawBack = false; DrawBottom = false; DrawTop = false;
}
}
public partial class MapRenderer : IDisposable {
Game game;
IGraphicsApi gfx;
internal int _1DUsed = -1;
internal int renderCount = 0;
internal ChunkInfo[] chunks, renderChunks, unsortedChunks;
internal bool[] usedTranslucent, usedNormal;
internal bool[] pendingTranslucent, pendingNormal;
internal int[] totalUsed;
internal ChunkUpdater updater;
bool inTranslucent = false;
public MapRenderer(Game game) {
this.game = game;
gfx = game.Graphics;
updater = new ChunkUpdater(game, this);
SetMeshBuilder(DefaultMeshBuilder());
}
public void Dispose() { updater.Dispose(); }
public void Refresh() { updater.Refresh(); }
public void RedrawBlock(int x, int y, int z, byte block, int oldHeight, int newHeight) {
updater.RedrawBlock(x, y, z, block, oldHeight, newHeight);
}
public void Render(double deltaTime) {
if (chunks == null) return;
ChunkSorter.UpdateSortOrder(game, updater);
updater.UpdateChunks(deltaTime);
RenderNormalChunks(deltaTime);
}
public void RenderTranslucent(double deltaTime) {
if (chunks == null) return;
RenderTranslucentChunks(deltaTime);
}
public void SetMeshBuilder(ChunkMeshBuilder newBuilder) {
if (updater.builder != null) updater.builder.Dispose();
updater.builder = newBuilder;
updater.builder.Init(game);
updater.builder.OnNewMapLoaded();
}
public ChunkMeshBuilder DefaultMeshBuilder() {
if (game.SmoothLighting)
return new AdvLightingMeshBuilder();
return new NormalMeshBuilder();
}
// Render solid and fully transparent to fill depth buffer.
// These blocks are treated as having an alpha value of either none or full.
void RenderNormalChunks(double deltaTime) {
int[] texIds = game.TerrainAtlas1D.TexIds;
gfx.SetBatchFormat(VertexFormat.P3fT2fC4b);
gfx.Texturing = true;
gfx.AlphaTest = true;
for (int batch = 0; batch < _1DUsed; batch++) {
if (totalUsed[batch] <= 0) continue;
if (pendingNormal[batch] || usedNormal[batch]) {
gfx.BindTexture(texIds[batch]);
RenderNormalBatch(batch);
pendingNormal[batch] = false;
}
}
CheckWeather(deltaTime);
gfx.AlphaTest = false;
gfx.Texturing = false;
#if DEBUG_OCCLUSION
DebugPickedPos();
#endif
}
void CheckWeather(double deltaTime) {
WorldEnv env = game.World.Env;
Vector3 pos = game.CurrentCameraPos;
Vector3I coords = Vector3I.Floor(pos);
byte block = game.World.SafeGetBlock(coords);
bool outside = !game.World.IsValidPos(Vector3I.Floor(pos));
inTranslucent = game.BlockInfo.Draw[block] == DrawType.Translucent
|| (pos.Y < env.EdgeHeight && outside);
// If we are under water, render weather before to blend properly
if (!inTranslucent || env.Weather == Weather.Sunny) return;
gfx.AlphaBlending = true;
game.WeatherRenderer.Render(deltaTime);
gfx.AlphaBlending = false;
}
// Render translucent(liquid) blocks. These 'blend' into other blocks.
void RenderTranslucentChunks(double deltaTime) {
// First fill depth buffer
int[] texIds = game.TerrainAtlas1D.TexIds;
gfx.SetBatchFormat(VertexFormat.P3fT2fC4b);
gfx.Texturing = false;
gfx.AlphaBlending = false;
gfx.ColourWrite = false;
for (int batch = 0; batch < _1DUsed; batch++) {
if (totalUsed[batch] <= 0) continue;
if (pendingTranslucent[batch] || usedTranslucent[batch]) {
RenderTranslucentBatchDepthPass(batch);
pendingTranslucent[batch] = false;
}
}
// Then actually draw the transluscent blocks
gfx.AlphaBlending = true;
gfx.Texturing = true;
gfx.ColourWrite = true;
gfx.DepthWrite = false; // we already calculated depth values in depth pass
for (int batch = 0; batch < _1DUsed; batch++) {
if (totalUsed[batch] <= 0) continue;
if (!usedTranslucent[batch]) continue;
gfx.BindTexture(texIds[batch]);
RenderTranslucentBatch(batch);
}
gfx.DepthWrite = true;
// If we weren't under water, render weather after to blend properly
if (!inTranslucent && game.World.Env.Weather != Weather.Sunny) {
gfx.AlphaTest = true;
game.WeatherRenderer.Render(deltaTime);
gfx.AlphaTest = false;
}
gfx.AlphaBlending = false;
gfx.Texturing = false;
}
const DrawMode mode = DrawMode.Triangles;
const int maxVertex = 65536;
const int maxIndices = maxVertex / 4 * 6;
void RenderNormalBatch(int batch) {
for (int i = 0; i < renderCount; i++) {
ChunkInfo info = renderChunks[i];
if (info.NormalParts == null) continue;
ChunkPartInfo part = info.NormalParts[batch];
if (part.IndicesCount == 0) continue;
usedNormal[batch] = true;
if (part.IndicesCount > maxIndices)
DrawBigPart(info, ref part);
else
DrawPart(info, ref part);
if (part.SpriteCount > 0) {
int count = part.SpriteCount / 4; // 4 per sprite
gfx.FaceCulling = true;
if (info.DrawRight || info.DrawFront) {
gfx.DrawIndexedVb_TrisT2fC4b(count, 0); game.Vertices += count;
}
if (info.DrawLeft || info.DrawBack) {
gfx.DrawIndexedVb_TrisT2fC4b(count, count); game.Vertices += count;
}
if (info.DrawLeft || info.DrawFront) {
gfx.DrawIndexedVb_TrisT2fC4b(count, count * 2); game.Vertices += count;
}
if (info.DrawRight || info.DrawBack) {
gfx.DrawIndexedVb_TrisT2fC4b(count, count * 3); game.Vertices += count;
}
gfx.FaceCulling = false;
}
}
}
void RenderTranslucentBatch(int batch) {
for (int i = 0; i < renderCount; i++) {
ChunkInfo info = renderChunks[i];
if (info.TranslucentParts == null) continue;
ChunkPartInfo part = info.TranslucentParts[batch];
if (part.IndicesCount == 0) continue;
DrawTranslucentPart(info, ref part, 1);
}
}
void RenderTranslucentBatchDepthPass(int batch) {
for (int i = 0; i < renderCount; i++) {
ChunkInfo info = renderChunks[i];
if (info.TranslucentParts == null) continue;
ChunkPartInfo part = info.TranslucentParts[batch];
if (part.IndicesCount == 0) continue;
usedTranslucent[batch] = true;
DrawTranslucentPart(info, ref part, 0);
}
}
void DrawPart(ChunkInfo info, ref ChunkPartInfo part) {
gfx.BindVb(part.VbId);
bool drawLeft = info.DrawLeft && part.LeftCount > 0;
bool drawRight = info.DrawRight && part.RightCount > 0;
bool drawBottom = info.DrawBottom && part.BottomCount > 0;
bool drawTop = info.DrawTop && part.TopCount > 0;
bool drawFront = info.DrawFront && part.FrontCount > 0;
bool drawBack = info.DrawBack && part.BackCount > 0;
if (drawLeft && drawRight) {
gfx.FaceCulling = true;
gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex);
gfx.FaceCulling = false;
game.Vertices += part.LeftCount + part.RightCount;
} else if (drawLeft) {
gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex);
game.Vertices += part.LeftCount;
} else if (drawRight) {
gfx.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex);
game.Vertices += part.RightCount;
}
if (drawFront && drawBack) {
gfx.FaceCulling = true;
gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex);
gfx.FaceCulling = false;
game.Vertices += part.FrontCount + part.BackCount;
} else if (drawFront) {
gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex);
game.Vertices += part.FrontCount;
} else if (drawBack) {
gfx.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex);
game.Vertices += part.BackCount;
}
if (drawBottom && drawTop) {
gfx.FaceCulling = true;
gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex);
gfx.FaceCulling = false;
game.Vertices += part.BottomCount + part.TopCount;
} else if (drawBottom) {
gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex);
game.Vertices += part.BottomCount;
} else if (drawTop) {
gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex);
game.Vertices += part.TopCount;
}
}
void DrawTranslucentPart(ChunkInfo info, ref ChunkPartInfo part, int m) {
gfx.BindVb(part.VbId);
bool drawLeft = (inTranslucent || info.DrawLeft) && part.LeftCount > 0;
bool drawRight = (inTranslucent || info.DrawRight) && part.RightCount > 0;
bool drawBottom = (inTranslucent || info.DrawBottom) && part.BottomCount > 0;
bool drawTop = (inTranslucent || info.DrawTop) && part.TopCount > 0;
bool drawFront = (inTranslucent || info.DrawFront) && part.FrontCount > 0;
bool drawBack = (inTranslucent || info.DrawBack) && part.BackCount > 0;
if (drawLeft && drawRight) {
gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex);
game.Vertices += m * (part.LeftCount + part.RightCount);
} else if (drawLeft) {
gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex);
game.Vertices += m * part.LeftCount;
} else if (drawRight) {
gfx.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex);
game.Vertices += m * part.RightCount;
}
if (drawFront && drawBack) {
gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex);
game.Vertices += m * (part.FrontCount + part.BackCount);
} else if (drawFront) {
gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex);
game.Vertices += m * part.FrontCount;
} else if (drawBack) {
gfx.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex);
game.Vertices += m * part.BackCount;
}
if (drawBottom && drawTop) {
gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex);
game.Vertices += m * (part.BottomCount + part.TopCount);
} else if (drawBottom) {
gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex);
game.Vertices += m * part.BottomCount;
} else if (drawTop) {
gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex);
game.Vertices += m * part.TopCount;
}
}
void DrawBigPart(ChunkInfo info, ref ChunkPartInfo part) {
gfx.BindVb(part.VbId);
bool drawLeft = info.DrawLeft && part.LeftCount > 0;
bool drawRight = info.DrawRight && part.RightCount > 0;
bool drawBottom = info.DrawBottom && part.BottomCount > 0;
bool drawTop = info.DrawTop && part.TopCount > 0;
bool drawFront = info.DrawFront && part.FrontCount > 0;
bool drawBack = info.DrawBack && part.BackCount > 0;
if (drawLeft && drawRight) {
gfx.FaceCulling = true;
gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount + part.RightCount, part.LeftIndex);
gfx.FaceCulling = false;
game.Vertices += part.LeftCount + part.RightCount;
} else if (drawLeft) {
gfx.DrawIndexedVb_TrisT2fC4b(part.LeftCount, part.LeftIndex);
game.Vertices += part.LeftCount;
} else if (drawRight) {
gfx.DrawIndexedVb_TrisT2fC4b(part.RightCount, part.RightIndex);
game.Vertices += part.RightCount;
}
if (drawFront && drawBack) {
gfx.FaceCulling = true;
gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount + part.BackCount, part.FrontIndex);
gfx.FaceCulling = false;
game.Vertices += part.FrontCount + part.BackCount;
} else if (drawFront) {
gfx.DrawIndexedVb_TrisT2fC4b(part.FrontCount, part.FrontIndex);
game.Vertices += part.FrontCount;
} else if (drawBack) {
gfx.DrawIndexedVb_TrisT2fC4b(part.BackCount, part.BackIndex);
game.Vertices += part.BackCount;
}
// Special handling for top and bottom as these can go over 65536 vertices and we need to adjust the indices in this case.
if (drawBottom && drawTop) {
gfx.FaceCulling = true;
if (part.IndicesCount > maxIndices) {
int part1Count = maxIndices - part.BottomIndex;
gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.BottomIndex);
gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount - part1Count, maxVertex, 0);
} else {
gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount + part.TopCount, part.BottomIndex);
}
gfx.FaceCulling = false;
game.Vertices += part.TopCount + part.BottomCount;
} else if (drawBottom) {
int part1Count;
if (part.IndicesCount > maxIndices &&
(part1Count = maxIndices - part.BottomIndex) < part.BottomCount) {
gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.BottomIndex);
gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount - part1Count, maxVertex, 0);
} else {
gfx.DrawIndexedVb_TrisT2fC4b(part.BottomCount, part.BottomIndex);
}
game.Vertices += part.BottomCount;
} else if (drawTop) {
int part1Count;
if (part.IndicesCount > maxIndices &&
(part1Count = maxIndices - part.TopIndex) < part.TopCount) {
gfx.DrawIndexedVb_TrisT2fC4b(part1Count, part.TopIndex);
gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount - part1Count, maxVertex, 0);
} else {
gfx.DrawIndexedVb_TrisT2fC4b(part.TopCount, part.TopIndex);
}
game.Vertices += part.TopCount;
}
}
}
}