mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-24 01:52:24 -05:00
Combine ChunkUpdater and MapRenderer
This commit is contained in:
parent
989a504cf6
commit
5fbd10b331
12 changed files with 668 additions and 685 deletions
|
@ -9,7 +9,6 @@
|
|||
#include "ErrorHandler.h"
|
||||
#include "Drawer.h"
|
||||
#include "ExtMath.h"
|
||||
#include "ChunkUpdater.h"
|
||||
#include "BlockID.h"
|
||||
#include "Block.h"
|
||||
#include "PackedCol.h"
|
||||
|
@ -340,7 +339,7 @@ static bool Builder_BuildChunk(int x1, int y1, int z1, bool* allAir) {
|
|||
|
||||
void Builder_MakeChunk(struct ChunkInfo* info) {
|
||||
int x = info->CentreX - 8, y = info->CentreY - 8, z = info->CentreZ - 8;
|
||||
bool allAir, hasMesh, hasNorm, hasTrans;
|
||||
bool allAir, hasMesh, hasNorm, hasTran;
|
||||
int totalVerts, partsIndex;
|
||||
int i, j, curIdx, offset;
|
||||
|
||||
|
@ -357,22 +356,22 @@ void Builder_MakeChunk(struct ChunkInfo* info) {
|
|||
#endif
|
||||
|
||||
partsIndex = MapRenderer_Pack(x >> CHUNK_SHIFT, y >> CHUNK_SHIFT, z >> CHUNK_SHIFT);
|
||||
offset = 0;
|
||||
hasNorm = false;
|
||||
hasTrans = false;
|
||||
offset = 0;
|
||||
hasNorm = false;
|
||||
hasTran = false;
|
||||
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++) {
|
||||
j = i + ATLAS1D_MAX_ATLASES;
|
||||
curIdx = partsIndex + i * MapRenderer_ChunksCount;
|
||||
|
||||
Builder_SetPartInfo(&Builder_Parts[i], &offset, &MapRenderer_PartsNormal[curIdx], &hasNorm);
|
||||
Builder_SetPartInfo(&Builder_Parts[j], &offset, &MapRenderer_PartsTranslucent[curIdx], &hasTrans);
|
||||
Builder_SetPartInfo(&Builder_Parts[j], &offset, &MapRenderer_PartsTranslucent[curIdx], &hasTran);
|
||||
}
|
||||
|
||||
if (hasNorm) {
|
||||
info->NormalParts = &MapRenderer_PartsNormal[partsIndex];
|
||||
}
|
||||
if (hasTrans) {
|
||||
if (hasTran) {
|
||||
info->TranslucentParts = &MapRenderer_PartsTranslucent[partsIndex];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,523 +0,0 @@
|
|||
#include "ChunkUpdater.h"
|
||||
#include "Constants.h"
|
||||
#include "Event.h"
|
||||
#include "ExtMath.h"
|
||||
#include "Funcs.h"
|
||||
#include "Game.h"
|
||||
#include "Graphics.h"
|
||||
#include "Entity.h"
|
||||
#include "MapRenderer.h"
|
||||
#include "Platform.h"
|
||||
#include "TerrainAtlas.h"
|
||||
#include "World.h"
|
||||
#include "Builder.h"
|
||||
#include "Utils.h"
|
||||
#include "ErrorHandler.h"
|
||||
#include "Camera.h"
|
||||
|
||||
static Vector3I ChunkUpdater_ChunkPos;
|
||||
static uint32_t* ChunkUpdater_Distances;
|
||||
|
||||
void ChunkInfo_Reset(struct ChunkInfo* chunk, int x, int y, int z) {
|
||||
chunk->CentreX = x + 8; chunk->CentreY = y + 8; chunk->CentreZ = z + 8;
|
||||
#ifndef CC_BUILD_GL11
|
||||
chunk->Vb = GFX_NULL;
|
||||
#endif
|
||||
|
||||
chunk->Visible = true; chunk->Empty = false;
|
||||
chunk->PendingDelete = false; chunk->AllAir = false;
|
||||
chunk->DrawXMin = false; chunk->DrawXMax = false; chunk->DrawZMin = false;
|
||||
chunk->DrawZMax = false; chunk->DrawYMin = false; chunk->DrawYMax = false;
|
||||
|
||||
chunk->NormalParts = NULL;
|
||||
chunk->TranslucentParts = NULL;
|
||||
}
|
||||
|
||||
static int cu_chunksTarget = 12;
|
||||
#define cu_targetTime ((1.0 / 30) + 0.01)
|
||||
static Vector3 cu_lastCamPos;
|
||||
static float cu_lastHeadY, cu_lastHeadX;
|
||||
static int cu_elementsPerBitmap;
|
||||
|
||||
static void ChunkUpdater_EnvVariableChanged(void* obj, int envVar) {
|
||||
if (envVar == ENV_VAR_SUN_COL || envVar == ENV_VAR_SHADOW_COL) {
|
||||
ChunkUpdater_Refresh();
|
||||
} else if (envVar == ENV_VAR_EDGE_HEIGHT || envVar == ENV_VAR_SIDES_OFFSET) {
|
||||
int oldClip = Builder_EdgeLevel;
|
||||
Builder_SidesLevel = max(0, Env_SidesHeight);
|
||||
Builder_EdgeLevel = max(0, Env_EdgeHeight);
|
||||
|
||||
/* Only need to refresh chunks on map borders up to highest edge level.*/
|
||||
ChunkUpdater_RefreshBorders(max(oldClip, Builder_EdgeLevel));
|
||||
}
|
||||
}
|
||||
|
||||
static void ChunkUpdater_TerrainAtlasChanged(void* obj) {
|
||||
if (MapRenderer_1DUsedCount) {
|
||||
bool refreshRequired = cu_elementsPerBitmap != Atlas1D_TilesPerAtlas;
|
||||
if (refreshRequired) ChunkUpdater_Refresh();
|
||||
}
|
||||
|
||||
MapRenderer_1DUsedCount = Atlas1D_UsedAtlasesCount();
|
||||
cu_elementsPerBitmap = Atlas1D_TilesPerAtlas;
|
||||
ChunkUpdater_ResetPartFlags();
|
||||
}
|
||||
|
||||
static void ChunkUpdater_BlockDefinitionChanged(void* obj) {
|
||||
ChunkUpdater_Refresh();
|
||||
MapRenderer_1DUsedCount = Atlas1D_UsedAtlasesCount();
|
||||
ChunkUpdater_ResetPartFlags();
|
||||
}
|
||||
|
||||
static void ChunkUpdater_ProjectionChanged(void* obj) {
|
||||
cu_lastCamPos = Vector3_BigPos();
|
||||
}
|
||||
|
||||
static void ChunkUpdater_ViewDistanceChanged(void* obj) {
|
||||
cu_lastCamPos = Vector3_BigPos();
|
||||
}
|
||||
|
||||
|
||||
static void ChunkUpdater_FreePartsAllocations(void) {
|
||||
Mem_Free(MapRenderer_PartsBuffer_Raw);
|
||||
MapRenderer_PartsBuffer_Raw = NULL;
|
||||
MapRenderer_PartsNormal = NULL;
|
||||
MapRenderer_PartsTranslucent = NULL;
|
||||
}
|
||||
|
||||
static void ChunkUpdater_FreeAllocations(void) {
|
||||
if (!MapRenderer_Chunks) return;
|
||||
ChunkUpdater_FreePartsAllocations();
|
||||
|
||||
Mem_Free(MapRenderer_Chunks);
|
||||
Mem_Free(MapRenderer_SortedChunks);
|
||||
Mem_Free(MapRenderer_RenderChunks);
|
||||
Mem_Free(ChunkUpdater_Distances);
|
||||
|
||||
MapRenderer_Chunks = NULL;
|
||||
MapRenderer_SortedChunks = NULL;
|
||||
MapRenderer_RenderChunks = NULL;
|
||||
ChunkUpdater_Distances = NULL;
|
||||
}
|
||||
|
||||
static void ChunkUpdater_PerformPartsAllocations(void) {
|
||||
uint32_t count = MapRenderer_ChunksCount * MapRenderer_1DUsedCount;
|
||||
MapRenderer_PartsBuffer_Raw = Mem_AllocCleared(count * 2, sizeof(struct ChunkPartInfo), "chunk parts");
|
||||
MapRenderer_PartsNormal = MapRenderer_PartsBuffer_Raw;
|
||||
MapRenderer_PartsTranslucent = MapRenderer_PartsBuffer_Raw + count;
|
||||
}
|
||||
|
||||
static void ChunkUpdater_PerformAllocations(void) {
|
||||
MapRenderer_Chunks = Mem_Alloc(MapRenderer_ChunksCount, sizeof(struct ChunkInfo), "chunk info");
|
||||
MapRenderer_SortedChunks = Mem_Alloc(MapRenderer_ChunksCount, sizeof(struct ChunkInfo*), "sorted chunk info");
|
||||
MapRenderer_RenderChunks = Mem_Alloc(MapRenderer_ChunksCount, sizeof(struct ChunkInfo*), "render chunk info");
|
||||
ChunkUpdater_Distances = Mem_Alloc(MapRenderer_ChunksCount, 4, "chunk distances");
|
||||
ChunkUpdater_PerformPartsAllocations();
|
||||
}
|
||||
|
||||
void ChunkUpdater_Refresh(void) {
|
||||
int oldCount;
|
||||
ChunkUpdater_ChunkPos = Vector3I_MaxValue();
|
||||
|
||||
if (MapRenderer_Chunks && World_Blocks) {
|
||||
ChunkUpdater_ClearChunkCache();
|
||||
ChunkUpdater_ResetChunkCache();
|
||||
|
||||
oldCount = MapRenderer_1DUsedCount;
|
||||
MapRenderer_1DUsedCount = Atlas1D_UsedAtlasesCount();
|
||||
/* Need to reallocate parts array in this case */
|
||||
if (MapRenderer_1DUsedCount != oldCount) {
|
||||
ChunkUpdater_FreePartsAllocations();
|
||||
ChunkUpdater_PerformPartsAllocations();
|
||||
}
|
||||
}
|
||||
ChunkUpdater_ResetPartCounts();
|
||||
}
|
||||
static void ChunkUpdater_Refresh_Handler(void* obj) {
|
||||
ChunkUpdater_Refresh();
|
||||
}
|
||||
|
||||
void ChunkUpdater_RefreshBorders(int clipLevel) {
|
||||
int cx, cy, cz;
|
||||
bool onBorder;
|
||||
|
||||
ChunkUpdater_ChunkPos = Vector3I_MaxValue();
|
||||
if (!MapRenderer_Chunks || !World_Blocks) return;
|
||||
|
||||
for (cz = 0; cz < MapRenderer_ChunksZ; cz++) {
|
||||
for (cy = 0; cy < MapRenderer_ChunksY; cy++) {
|
||||
for (cx = 0; cx < MapRenderer_ChunksX; cx++) {
|
||||
onBorder = cx == 0 || cz == 0 || cx == (MapRenderer_ChunksX - 1) || cz == (MapRenderer_ChunksZ - 1);
|
||||
|
||||
if (onBorder && (cy * CHUNK_SIZE) < clipLevel) {
|
||||
MapRenderer_RefreshChunk(cx, cy, cz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ChunkUpdater_ApplyMeshBuilder(void) {
|
||||
if (Game_SmoothLighting) {
|
||||
/* TODO: Implement advanced lighting builder.*/
|
||||
AdvBuilder_SetActive();
|
||||
} else {
|
||||
NormalBuilder_SetActive();
|
||||
}
|
||||
}
|
||||
|
||||
static void ChunkUpdater_OnNewMap(void* obj) {
|
||||
Game_ChunkUpdates = 0;
|
||||
ChunkUpdater_ClearChunkCache();
|
||||
ChunkUpdater_ResetPartCounts();
|
||||
ChunkUpdater_FreeAllocations();
|
||||
ChunkUpdater_ChunkPos = Vector3I_MaxValue();
|
||||
}
|
||||
|
||||
static void ChunkUpdater_OnNewMapLoaded(void* obj) {
|
||||
int count;
|
||||
MapRenderer_ChunksX = (World_Width + CHUNK_MAX) >> CHUNK_SHIFT;
|
||||
MapRenderer_ChunksY = (World_Height + CHUNK_MAX) >> CHUNK_SHIFT;
|
||||
MapRenderer_ChunksZ = (World_Length + CHUNK_MAX) >> CHUNK_SHIFT;
|
||||
|
||||
count = MapRenderer_ChunksX * MapRenderer_ChunksY * MapRenderer_ChunksZ;
|
||||
/* TODO: Only perform reallocation when map volume has changed */
|
||||
/*if (MapRenderer_ChunksCount != count) { */
|
||||
MapRenderer_ChunksCount = count;
|
||||
ChunkUpdater_FreeAllocations();
|
||||
ChunkUpdater_PerformAllocations();
|
||||
/*}*/
|
||||
|
||||
ChunkUpdater_CreateChunkCache();
|
||||
Builder_OnNewMapLoaded();
|
||||
cu_lastCamPos = Vector3_BigPos();
|
||||
}
|
||||
|
||||
|
||||
static int ChunkUpdater_AdjustViewDist(int dist) {
|
||||
if (dist < CHUNK_SIZE) dist = CHUNK_SIZE;
|
||||
dist = Utils_AdjViewDist(dist);
|
||||
return (dist + 24) * (dist + 24);
|
||||
}
|
||||
|
||||
static int ChunkUpdater_UpdateChunksAndVisibility(int* chunkUpdates) {
|
||||
int viewDistSqr = ChunkUpdater_AdjustViewDist(Game_ViewDistance);
|
||||
int userDistSqr = ChunkUpdater_AdjustViewDist(Game_UserViewDistance);
|
||||
|
||||
struct ChunkInfo* info;
|
||||
int i, j = 0, distSqr;
|
||||
bool noData;
|
||||
|
||||
for (i = 0; i < MapRenderer_ChunksCount; i++) {
|
||||
info = MapRenderer_SortedChunks[i];
|
||||
if (info->Empty) continue;
|
||||
|
||||
distSqr = ChunkUpdater_Distances[i];
|
||||
noData = !info->NormalParts && !info->TranslucentParts;
|
||||
|
||||
/* Unload chunks beyond visible range */
|
||||
if (!noData && distSqr >= userDistSqr + 32 * 16) {
|
||||
ChunkUpdater_DeleteChunk(info); continue;
|
||||
}
|
||||
noData |= info->PendingDelete;
|
||||
|
||||
if (noData && distSqr <= viewDistSqr && *chunkUpdates < cu_chunksTarget) {
|
||||
ChunkUpdater_DeleteChunk(info);
|
||||
ChunkUpdater_BuildChunk(info, chunkUpdates);
|
||||
}
|
||||
|
||||
info->Visible = distSqr <= viewDistSqr &&
|
||||
FrustumCulling_SphereInFrustum(info->CentreX, info->CentreY, info->CentreZ, 14); /* 14 ~ sqrt(3 * 8^2) */
|
||||
if (info->Visible && !info->Empty) { MapRenderer_RenderChunks[j] = info; j++; }
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
static int ChunkUpdater_UpdateChunksStill(int* chunkUpdates) {
|
||||
int viewDistSqr = ChunkUpdater_AdjustViewDist(Game_ViewDistance);
|
||||
int userDistSqr = ChunkUpdater_AdjustViewDist(Game_UserViewDistance);
|
||||
|
||||
struct ChunkInfo* info;
|
||||
int i, j = 0, distSqr;
|
||||
bool noData;
|
||||
|
||||
for (i = 0; i < MapRenderer_ChunksCount; i++) {
|
||||
info = MapRenderer_SortedChunks[i];
|
||||
if (info->Empty) continue;
|
||||
|
||||
distSqr = ChunkUpdater_Distances[i];
|
||||
noData = !info->NormalParts && !info->TranslucentParts;
|
||||
|
||||
/* Unload chunks beyond visible range */
|
||||
if (!noData && distSqr >= userDistSqr + 32 * 16) {
|
||||
ChunkUpdater_DeleteChunk(info); continue;
|
||||
}
|
||||
noData |= info->PendingDelete;
|
||||
|
||||
if (noData && distSqr <= userDistSqr && *chunkUpdates < cu_chunksTarget) {
|
||||
ChunkUpdater_DeleteChunk(info);
|
||||
ChunkUpdater_BuildChunk(info, chunkUpdates);
|
||||
|
||||
/* only need to update the visibility of chunks in range. */
|
||||
info->Visible = distSqr <= viewDistSqr &&
|
||||
FrustumCulling_SphereInFrustum(info->CentreX, info->CentreY, info->CentreZ, 14); /* 14 ~ sqrt(3 * 8^2) */
|
||||
if (info->Visible && !info->Empty) { MapRenderer_RenderChunks[j] = info; j++; }
|
||||
} else if (info->Visible) {
|
||||
MapRenderer_RenderChunks[j] = info; j++;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
static void ChunkUpdater_UpdateChunks(double delta) {
|
||||
struct LocalPlayer* p;
|
||||
bool samePos;
|
||||
int chunkUpdates = 0;
|
||||
|
||||
/* Build more chunks if 30 FPS or over, otherwise slowdown */
|
||||
cu_chunksTarget += delta < cu_targetTime ? 1 : -1;
|
||||
Math_Clamp(cu_chunksTarget, 4, Game_MaxChunkUpdates);
|
||||
|
||||
p = &LocalPlayer_Instance;
|
||||
samePos = Vector3_Equals(&Camera_CurrentPos, &cu_lastCamPos)
|
||||
&& p->Base.HeadX == cu_lastHeadX && p->Base.HeadY == cu_lastHeadY;
|
||||
|
||||
MapRenderer_RenderChunksCount = samePos ?
|
||||
ChunkUpdater_UpdateChunksStill(&chunkUpdates) :
|
||||
ChunkUpdater_UpdateChunksAndVisibility(&chunkUpdates);
|
||||
|
||||
cu_lastCamPos = Camera_CurrentPos;
|
||||
cu_lastHeadX = p->Base.HeadX;
|
||||
cu_lastHeadY = p->Base.HeadY;
|
||||
|
||||
if (!samePos || chunkUpdates) {
|
||||
ChunkUpdater_ResetPartFlags();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ChunkUpdater_ResetPartFlags(void) {
|
||||
int i;
|
||||
for (i = 0; i < ATLAS1D_MAX_ATLASES; i++) {
|
||||
MapRenderer_CheckingNormalParts[i] = true;
|
||||
MapRenderer_HasNormalParts[i] = false;
|
||||
MapRenderer_CheckingTranslucentParts[i] = true;
|
||||
MapRenderer_HasTranslucentParts[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkUpdater_ResetPartCounts(void) {
|
||||
int i;
|
||||
for (i = 0; i < ATLAS1D_MAX_ATLASES; i++) {
|
||||
MapRenderer_NormalPartsCount[i] = 0;
|
||||
MapRenderer_TranslucentPartsCount[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkUpdater_CreateChunkCache(void) {
|
||||
int x, y, z, index = 0;
|
||||
for (z = 0; z < World_Length; z += CHUNK_SIZE) {
|
||||
for (y = 0; y < World_Height; y += CHUNK_SIZE) {
|
||||
for (x = 0; x < World_Width; x += CHUNK_SIZE) {
|
||||
ChunkInfo_Reset(&MapRenderer_Chunks[index], x, y, z);
|
||||
MapRenderer_SortedChunks[index] = &MapRenderer_Chunks[index];
|
||||
MapRenderer_RenderChunks[index] = &MapRenderer_Chunks[index];
|
||||
ChunkUpdater_Distances[index] = 0;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkUpdater_ResetChunkCache(void) {
|
||||
int x, y, z, index = 0;
|
||||
for (z = 0; z < World_Length; z += CHUNK_SIZE) {
|
||||
for (y = 0; y < World_Height; y += CHUNK_SIZE) {
|
||||
for (x = 0; x < World_Width; x += CHUNK_SIZE) {
|
||||
ChunkInfo_Reset(&MapRenderer_Chunks[index], x, y, z);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkUpdater_ClearChunkCache(void) {
|
||||
int i;
|
||||
if (!MapRenderer_Chunks) return;
|
||||
|
||||
for (i = 0; i < MapRenderer_ChunksCount; i++) {
|
||||
ChunkUpdater_DeleteChunk(&MapRenderer_Chunks[i]);
|
||||
}
|
||||
ChunkUpdater_ResetPartCounts();
|
||||
}
|
||||
static void ChunkUpdater_ClearChunkCache_Handler(void* obj) {
|
||||
ChunkUpdater_ClearChunkCache();
|
||||
}
|
||||
|
||||
|
||||
void ChunkUpdater_DeleteChunk(struct ChunkInfo* info) {
|
||||
struct ChunkPartInfo* ptr;
|
||||
int i;
|
||||
|
||||
info->Empty = false; info->AllAir = false;
|
||||
#ifdef OCCLUSION
|
||||
info.OcclusionFlags = 0;
|
||||
info.OccludedFlags = 0;
|
||||
#endif
|
||||
#ifndef CC_BUILD_GL11
|
||||
Gfx_DeleteVb(&info->Vb);
|
||||
#endif
|
||||
|
||||
if (info->NormalParts) {
|
||||
ptr = info->NormalParts;
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += MapRenderer_ChunksCount) {
|
||||
if (ptr->Offset < 0) continue;
|
||||
MapRenderer_NormalPartsCount[i]--;
|
||||
#ifdef CC_BUILD_GL11
|
||||
Gfx_DeleteVb(&ptr->Vb);
|
||||
#endif
|
||||
}
|
||||
info->NormalParts = NULL;
|
||||
}
|
||||
|
||||
if (info->TranslucentParts) {
|
||||
ptr = info->TranslucentParts;
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += MapRenderer_ChunksCount) {
|
||||
if (ptr->Offset < 0) continue;
|
||||
MapRenderer_TranslucentPartsCount[i]--;
|
||||
#ifdef CC_BUILD_GL11
|
||||
Gfx_DeleteVb(&ptr->Vb);
|
||||
#endif
|
||||
}
|
||||
info->TranslucentParts = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkUpdater_BuildChunk(struct ChunkInfo* info, int* chunkUpdates) {
|
||||
struct ChunkPartInfo* ptr;
|
||||
int i;
|
||||
|
||||
Game_ChunkUpdates++;
|
||||
(*chunkUpdates)++;
|
||||
info->PendingDelete = false;
|
||||
Builder_MakeChunk(info);
|
||||
|
||||
if (!info->NormalParts && !info->TranslucentParts) {
|
||||
info->Empty = true; return;
|
||||
}
|
||||
|
||||
if (info->NormalParts) {
|
||||
ptr = info->NormalParts;
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += MapRenderer_ChunksCount) {
|
||||
if (ptr->Offset >= 0) { MapRenderer_NormalPartsCount[i]++; }
|
||||
}
|
||||
}
|
||||
|
||||
if (info->TranslucentParts) {
|
||||
ptr = info->TranslucentParts;
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += MapRenderer_ChunksCount) {
|
||||
if (ptr->Offset >= 0) { MapRenderer_TranslucentPartsCount[i]++; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ChunkUpdater_QuickSort(int left, int right) {
|
||||
struct ChunkInfo** values = MapRenderer_SortedChunks; struct ChunkInfo* value;
|
||||
uint32_t* keys = ChunkUpdater_Distances; uint32_t key;
|
||||
|
||||
while (left < right) {
|
||||
int i = left, j = right;
|
||||
uint32_t pivot = keys[(i + j) >> 1];
|
||||
|
||||
/* partition the list */
|
||||
while (i <= j) {
|
||||
while (pivot > keys[i]) i++;
|
||||
while (pivot < keys[j]) j--;
|
||||
QuickSort_Swap_KV_Maybe();
|
||||
}
|
||||
/* recurse into the smaller subset */
|
||||
QuickSort_Recurse(ChunkUpdater_QuickSort)
|
||||
}
|
||||
}
|
||||
|
||||
static void ChunkUpdater_UpdateSortOrder(void) {
|
||||
struct ChunkInfo* info;
|
||||
Vector3I pos;
|
||||
int dXMin, dXMax, dYMin, dYMax, dZMin, dZMax;
|
||||
int i, dx, dy, dz;
|
||||
|
||||
/* pos is centre coordinate of chunk camera is in */
|
||||
Vector3I_Floor(&pos, &Camera_CurrentPos);
|
||||
pos.X = (pos.X & ~CHUNK_MASK) + HALF_CHUNK_SIZE;
|
||||
pos.Y = (pos.Y & ~CHUNK_MASK) + HALF_CHUNK_SIZE;
|
||||
pos.Z = (pos.Z & ~CHUNK_MASK) + HALF_CHUNK_SIZE;
|
||||
|
||||
/* If in same chunk, don't need to recalculate sort order */
|
||||
if (Vector3I_Equals(&pos, &ChunkUpdater_ChunkPos)) return;
|
||||
ChunkUpdater_ChunkPos = pos;
|
||||
if (!MapRenderer_ChunksCount) return;
|
||||
|
||||
for (i = 0; i < MapRenderer_ChunksCount; i++) {
|
||||
info = MapRenderer_SortedChunks[i];
|
||||
/* Calculate distance to chunk centre */
|
||||
dx = info->CentreX - pos.X; dy = info->CentreY - pos.Y; dz = info->CentreZ - pos.Z;
|
||||
ChunkUpdater_Distances[i] = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
/* Can work out distance to chunk faces as offset from distance to chunk centre on each axis */
|
||||
dXMin = dx - HALF_CHUNK_SIZE; dXMax = dx + HALF_CHUNK_SIZE;
|
||||
dYMin = dy - HALF_CHUNK_SIZE; dYMax = dy + HALF_CHUNK_SIZE;
|
||||
dZMin = dz - HALF_CHUNK_SIZE; dZMax = dz + HALF_CHUNK_SIZE;
|
||||
|
||||
/* Back face culling: make sure that the chunk is definitely entirely back facing */
|
||||
info->DrawXMin = !(dXMin <= 0 && dXMax <= 0);
|
||||
info->DrawXMax = !(dXMin >= 0 && dXMax >= 0);
|
||||
info->DrawZMin = !(dZMin <= 0 && dZMax <= 0);
|
||||
info->DrawZMax = !(dZMin >= 0 && dZMax >= 0);
|
||||
info->DrawYMin = !(dYMin <= 0 && dYMax <= 0);
|
||||
info->DrawYMax = !(dYMin >= 0 && dYMax >= 0);
|
||||
}
|
||||
|
||||
ChunkUpdater_QuickSort(0, MapRenderer_ChunksCount - 1);
|
||||
ChunkUpdater_ResetPartFlags();
|
||||
/*SimpleOcclusionCulling();*/
|
||||
}
|
||||
|
||||
void ChunkUpdater_Update(double deltaTime) {
|
||||
if (!MapRenderer_Chunks) return;
|
||||
ChunkUpdater_UpdateSortOrder();
|
||||
ChunkUpdater_UpdateChunks(deltaTime);
|
||||
}
|
||||
|
||||
void ChunkUpdater_Init(void) {
|
||||
Event_RegisterVoid(&TextureEvents_AtlasChanged, NULL, ChunkUpdater_TerrainAtlasChanged);
|
||||
Event_RegisterVoid(&WorldEvents_NewMap, NULL, ChunkUpdater_OnNewMap);
|
||||
Event_RegisterVoid(&WorldEvents_MapLoaded, NULL, ChunkUpdater_OnNewMapLoaded);
|
||||
Event_RegisterInt(&WorldEvents_EnvVarChanged, NULL, ChunkUpdater_EnvVariableChanged);
|
||||
|
||||
Event_RegisterVoid(&BlockEvents_BlockDefChanged, NULL, ChunkUpdater_BlockDefinitionChanged);
|
||||
Event_RegisterVoid(&GfxEvents_ViewDistanceChanged, NULL, ChunkUpdater_ViewDistanceChanged);
|
||||
Event_RegisterVoid(&GfxEvents_ProjectionChanged, NULL, ChunkUpdater_ProjectionChanged);
|
||||
Event_RegisterVoid(&GfxEvents_ContextLost, NULL, ChunkUpdater_ClearChunkCache_Handler);
|
||||
Event_RegisterVoid(&GfxEvents_ContextRecreated, NULL, ChunkUpdater_Refresh_Handler);
|
||||
|
||||
/* This = 87 fixes map being invisible when no textures */
|
||||
MapRenderer_1DUsedCount = 87; /* Atlas1D_UsedAtlasesCount(); */
|
||||
ChunkUpdater_ChunkPos = Vector3I_MaxValue();
|
||||
ChunkUpdater_ApplyMeshBuilder();
|
||||
}
|
||||
|
||||
void ChunkUpdater_Free(void) {
|
||||
Event_UnregisterVoid(&TextureEvents_AtlasChanged, NULL, ChunkUpdater_TerrainAtlasChanged);
|
||||
Event_UnregisterVoid(&WorldEvents_NewMap, NULL, ChunkUpdater_OnNewMap);
|
||||
Event_UnregisterVoid(&WorldEvents_MapLoaded, NULL, ChunkUpdater_OnNewMapLoaded);
|
||||
Event_UnregisterInt(&WorldEvents_EnvVarChanged, NULL, ChunkUpdater_EnvVariableChanged);
|
||||
|
||||
Event_UnregisterVoid(&BlockEvents_BlockDefChanged, NULL, ChunkUpdater_BlockDefinitionChanged);
|
||||
Event_UnregisterVoid(&GfxEvents_ViewDistanceChanged, NULL, ChunkUpdater_ViewDistanceChanged);
|
||||
Event_UnregisterVoid(&GfxEvents_ProjectionChanged, NULL, ChunkUpdater_ProjectionChanged);
|
||||
Event_UnregisterVoid(&GfxEvents_ContextLost, NULL, ChunkUpdater_ClearChunkCache_Handler);
|
||||
Event_UnregisterVoid(&GfxEvents_ContextRecreated, NULL, ChunkUpdater_Refresh_Handler);
|
||||
|
||||
ChunkUpdater_OnNewMap(NULL);
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
#ifndef CC_CHUNKUPDATER_H
|
||||
#define CC_CHUNKUPDATER_H
|
||||
#include "Core.h"
|
||||
#include "Constants.h"
|
||||
/* Manages the process of building/deleting chunk meshes.
|
||||
Also sorts chunks so nearest chunks are ordered first, and calculates chunk visibility.
|
||||
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
*/
|
||||
|
||||
/* Describes a portion of the data needed for rendering a chunk. */
|
||||
struct ChunkPartInfo {
|
||||
#ifdef CC_BUILD_GL11
|
||||
GfxResourceID Vb;
|
||||
#endif
|
||||
int Offset; /* -1 if no vertices at all */
|
||||
int SpriteCount; /* Sprite vertices count */
|
||||
uint16_t Counts[FACE_COUNT]; /* Counts per face */
|
||||
};
|
||||
|
||||
/* Describes data necessary for rendering a chunk. */
|
||||
struct ChunkInfo {
|
||||
uint16_t CentreX, CentreY, CentreZ; /* Centre coordinates of the chunk */
|
||||
|
||||
uint8_t Visible : 1; /* Whether chunk is visibile to the player */
|
||||
uint8_t Empty : 1; /* Whether the chunk is empty of data */
|
||||
uint8_t PendingDelete : 1; /* Whether chunk is pending deletion*/
|
||||
uint8_t AllAir : 1; /* Whether chunk is completely air */
|
||||
uint8_t : 0; /* pad to next byte*/
|
||||
|
||||
uint8_t DrawXMin : 1;
|
||||
uint8_t DrawXMax : 1;
|
||||
uint8_t DrawZMin : 1;
|
||||
uint8_t DrawZMax : 1;
|
||||
uint8_t DrawYMin : 1;
|
||||
uint8_t DrawYMax : 1;
|
||||
uint8_t : 0; /* pad to next byte */
|
||||
#ifdef OCCLUSION
|
||||
public bool Visited = false, Occluded = false;
|
||||
public byte OcclusionFlags, OccludedFlags, DistanceFlags;
|
||||
#endif
|
||||
#ifndef CC_BUILD_GL11
|
||||
GfxResourceID Vb;
|
||||
#endif
|
||||
struct ChunkPartInfo* NormalParts;
|
||||
struct ChunkPartInfo* TranslucentParts;
|
||||
};
|
||||
|
||||
void ChunkInfo_Reset(struct ChunkInfo* chunk, int x, int y, int z);
|
||||
|
||||
void ChunkUpdater_Init(void);
|
||||
void ChunkUpdater_Free(void);
|
||||
void ChunkUpdater_Refresh(void);
|
||||
void ChunkUpdater_RefreshBorders(int clipLevel);
|
||||
void ChunkUpdater_ApplyMeshBuilder(void);
|
||||
void ChunkUpdater_Update(double deltaTime);
|
||||
|
||||
void ChunkUpdater_ResetPartFlags(void);
|
||||
void ChunkUpdater_ResetPartCounts(void);
|
||||
void ChunkUpdater_CreateChunkCache(void);
|
||||
void ChunkUpdater_ResetChunkCache(void);
|
||||
void ChunkUpdater_ClearChunkCache(void);
|
||||
|
||||
void ChunkUpdater_DeleteChunk(struct ChunkInfo* info);
|
||||
void ChunkUpdater_BuildChunk(struct ChunkInfo* info, int* chunkUpdates);
|
||||
#endif
|
|
@ -195,7 +195,6 @@
|
|||
<ClInclude Include="Builder.h" />
|
||||
<ClInclude Include="Camera.h" />
|
||||
<ClInclude Include="Chat.h" />
|
||||
<ClInclude Include="ChunkUpdater.h" />
|
||||
<ClInclude Include="Constants.h" />
|
||||
<ClInclude Include="Bitmap.h" />
|
||||
<ClInclude Include="Drawer.h" />
|
||||
|
@ -259,7 +258,6 @@
|
|||
<ClCompile Include="Block.c" />
|
||||
<ClCompile Include="Builder.c" />
|
||||
<ClCompile Include="Chat.c" />
|
||||
<ClCompile Include="ChunkUpdater.c" />
|
||||
<ClCompile Include="Bitmap.c" />
|
||||
<ClCompile Include="Drawer.c" />
|
||||
<ClCompile Include="Drawer2D.c" />
|
||||
|
|
|
@ -297,9 +297,6 @@
|
|||
<ClInclude Include="Model.h">
|
||||
<Filter>Header Files\Entities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ChunkUpdater.h">
|
||||
<Filter>Header Files\Rendering</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MapRenderer.h">
|
||||
<Filter>Header Files\Rendering</Filter>
|
||||
</ClInclude>
|
||||
|
@ -467,9 +464,6 @@
|
|||
<ClCompile Include="Model.c">
|
||||
<Filter>Source Files\Entities</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ChunkUpdater.c">
|
||||
<Filter>Source Files\Rendering</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MapRenderer.c">
|
||||
<Filter>Source Files\Rendering</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "Menus.h"
|
||||
#include "Audio.h"
|
||||
#include "Stream.h"
|
||||
#include "TerrainAtlas.h"
|
||||
|
||||
static struct IGameComponent Game_Components[26];
|
||||
static int Game_ComponentsCount;
|
||||
|
@ -321,7 +322,7 @@ static void Game_OnLowVRAMDetected(void* obj) {
|
|||
Game_UserViewDistance /= 2;
|
||||
Game_UserViewDistance = max(16, Game_UserViewDistance);
|
||||
|
||||
ChunkUpdater_Refresh();
|
||||
MapRenderer_Refresh();
|
||||
Game_SetViewDistance(Game_UserViewDistance);
|
||||
Chat_AddRaw("&cOut of VRAM! Halving view distance..");
|
||||
}
|
||||
|
@ -478,7 +479,7 @@ void Game_Load(void) {
|
|||
LocalPlayer_MakeComponent(&comp); Game_AddComponent(&comp);
|
||||
Entities_List[ENTITIES_SELF_ID] = &LocalPlayer_Instance.Base;
|
||||
|
||||
ChunkUpdater_Init();
|
||||
MapRenderer_Init();
|
||||
EnvRenderer_MakeComponent(&comp); Game_AddComponent(&comp);
|
||||
String_InitArray(renderType, renderTypeBuffer);
|
||||
Options_Get(OPT_RENDER_TYPE, &renderType, "normal");
|
||||
|
@ -591,7 +592,7 @@ static void Game_Render3D(double delta, float t) {
|
|||
EnvRenderer_RenderSky(delta);
|
||||
EnvRenderer_RenderClouds(delta);
|
||||
|
||||
ChunkUpdater_Update(delta);
|
||||
MapRenderer_Update(delta);
|
||||
MapRenderer_RenderNormal(delta);
|
||||
EnvRenderer_RenderMapSides(delta);
|
||||
|
||||
|
@ -724,7 +725,7 @@ static void Game_RenderFrame(double delta) {
|
|||
void Game_Free(void* obj) {
|
||||
int i;
|
||||
|
||||
ChunkUpdater_Free();
|
||||
MapRenderer_Free();
|
||||
Atlas2D_Free();
|
||||
Atlas1D_Free();
|
||||
ModelCache_Free();
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
struct IGameComponent {
|
||||
/* Called when the game is being loaded. */
|
||||
void (*Init)(void);
|
||||
/* Called when the component is being freed, due to game being closed. */
|
||||
/* Called when the component is being freed. (e.g. due to game being closed) */
|
||||
void (*Free)(void);
|
||||
/* Called when the texture pack has been loaded and all components have been initalised. */
|
||||
void (*Ready)(void);
|
||||
/* Called to reset the component's state when the user is reconnecting to a server */
|
||||
/* Called to reset the component's state. (e.g. reconnecting to server) */
|
||||
void (*Reset)(void);
|
||||
/* Called when the texture pack has been loaded and all components have been initialised. */
|
||||
void (*Ready)(void);
|
||||
/* Called to update the component's state when the user begins loading a new map. */
|
||||
void (*OnNewMap)(void);
|
||||
/* Called to update the component's state when the user has finished loading a new map. */
|
||||
|
|
|
@ -1,28 +1,71 @@
|
|||
#include "MapRenderer.h"
|
||||
#include "Block.h"
|
||||
#include "Builder.h"
|
||||
#include "Camera.h"
|
||||
#include "Entity.h"
|
||||
#include "EnvRenderer.h"
|
||||
#include "Event.h"
|
||||
#include "ExtMath.h"
|
||||
#include "Funcs.h"
|
||||
#include "Game.h"
|
||||
#include "Graphics.h"
|
||||
#include "EnvRenderer.h"
|
||||
#include "Platform.h"
|
||||
#include "TerrainAtlas.h"
|
||||
#include "Utils.h"
|
||||
#include "World.h"
|
||||
#include "Camera.h"
|
||||
#include "ChunkUpdater.h"
|
||||
|
||||
static bool inTranslucent;
|
||||
static int elementsPerBitmap;
|
||||
static Vector3I chunkPos;
|
||||
|
||||
/* The number of non-empty Normal/Translucent ChunkPartInfos (across entire world) for each 1D atlas batch. */
|
||||
/* 1D atlas batches that do not have any ChunkPartInfos can be entirely skipped. */
|
||||
static int normPartsCount[ATLAS1D_MAX_ATLASES], tranPartsCount[ATLAS1D_MAX_ATLASES];
|
||||
/* Whether there are any visible Normal/Translucent ChunkPartInfos for each 1D atlas batch. */
|
||||
/* 1D atlas batches that do not have any visible ChunkPartInfos can be skipped. */
|
||||
static bool hasNormParts[ATLAS1D_MAX_ATLASES], hasTranParts[ATLAS1D_MAX_ATLASES];
|
||||
/* Whether renderer should check if there are any visible Normal/Translucent ChunkPartInfos for each 1D atlas batch. */
|
||||
static bool checkNormParts[ATLAS1D_MAX_ATLASES], checkTranParts[ATLAS1D_MAX_ATLASES];
|
||||
|
||||
/* Render info for all chunks in the world. Unsorted. */
|
||||
static struct ChunkInfo* mapChunks;
|
||||
/* Pointers to render info for all chunks in the world, sorted by distance from the camera. */
|
||||
static struct ChunkInfo** sortedChunks;
|
||||
/* Pointers to render info for all chunks in the world, sorted by distance from the camera. */
|
||||
/* Only chunks that can be rendered (i.e. not empty and are visible) are included in this. */
|
||||
static struct ChunkInfo** renderChunks;
|
||||
/* Number of actually used pointers in the renderChunks array. Entries past this are ignored and skipped. */
|
||||
static int renderChunksCount;
|
||||
/* Distance of each chunk from the camera. */
|
||||
static uint32_t* distances;
|
||||
|
||||
/* Buffer for all chunk parts. There are (MapRenderer_ChunksCount * Atlas1D_Count) * 2 parts in the buffer,
|
||||
with parts for 'normal' buffer being in lower half. */
|
||||
static struct ChunkPartInfo* partsBuffer_Raw;
|
||||
|
||||
struct ChunkInfo* MapRenderer_GetChunk(int cx, int cy, int cz) {
|
||||
return &MapRenderer_Chunks[MapRenderer_Pack(cx, cy, cz)];
|
||||
return &mapChunks[MapRenderer_Pack(cx, cy, cz)];
|
||||
}
|
||||
|
||||
void MapRenderer_RefreshChunk(int cx, int cy, int cz) {
|
||||
struct ChunkInfo* info;
|
||||
if (cx < 0 || cy < 0 || cz < 0 || cx >= MapRenderer_ChunksX
|
||||
|| cy >= MapRenderer_ChunksY || cz >= MapRenderer_ChunksZ) return;
|
||||
void ChunkInfo_Reset(struct ChunkInfo* chunk, int x, int y, int z) {
|
||||
chunk->CentreX = x + 8; chunk->CentreY = y + 8; chunk->CentreZ = z + 8;
|
||||
#ifndef CC_BUILD_GL11
|
||||
chunk->Vb = GFX_NULL;
|
||||
#endif
|
||||
|
||||
info = &MapRenderer_Chunks[MapRenderer_Pack(cx, cy, cz)];
|
||||
if (info->AllAir) return; /* do not recreate chunks completely air */
|
||||
info->Empty = false;
|
||||
info->PendingDelete = true;
|
||||
chunk->Visible = true; chunk->Empty = false;
|
||||
chunk->PendingDelete = false; chunk->AllAir = false;
|
||||
chunk->DrawXMin = false; chunk->DrawXMax = false; chunk->DrawZMin = false;
|
||||
chunk->DrawZMax = false; chunk->DrawYMin = false; chunk->DrawYMax = false;
|
||||
|
||||
chunk->NormalParts = NULL;
|
||||
chunk->TranslucentParts = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*-------------------------------------------------------Map rendering-----------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
static void MapRenderer_CheckWeather(double delta) {
|
||||
Vector3I pos;
|
||||
BlockID block;
|
||||
|
@ -61,13 +104,13 @@ static void MapRenderer_RenderNormalBatch(int batch) {
|
|||
bool drawMin, drawMax;
|
||||
int i, offset, count;
|
||||
|
||||
for (i = 0; i < MapRenderer_RenderChunksCount; i++) {
|
||||
info = MapRenderer_RenderChunks[i];
|
||||
for (i = 0; i < renderChunksCount; i++) {
|
||||
info = renderChunks[i];
|
||||
if (!info->NormalParts) continue;
|
||||
|
||||
part = info->NormalParts[batchOffset];
|
||||
if (part.Offset < 0) continue;
|
||||
MapRenderer_HasNormalParts[batch] = true;
|
||||
hasNormParts[batch] = true;
|
||||
|
||||
#ifndef CC_BUILD_GL11
|
||||
Gfx_BindVb(info->Vb);
|
||||
|
@ -116,7 +159,7 @@ static void MapRenderer_RenderNormalBatch(int batch) {
|
|||
|
||||
void MapRenderer_RenderNormal(double delta) {
|
||||
int batch;
|
||||
if (!MapRenderer_Chunks) return;
|
||||
if (!mapChunks) return;
|
||||
|
||||
Gfx_SetBatchFormat(VERTEX_FORMAT_P3FT2FC4B);
|
||||
Gfx_SetTexturing(true);
|
||||
|
@ -124,11 +167,11 @@ void MapRenderer_RenderNormal(double delta) {
|
|||
|
||||
Gfx_EnableMipmaps();
|
||||
for (batch = 0; batch < MapRenderer_1DUsedCount; batch++) {
|
||||
if (MapRenderer_NormalPartsCount[batch] <= 0) continue;
|
||||
if (MapRenderer_HasNormalParts[batch] || MapRenderer_CheckingNormalParts[batch]) {
|
||||
if (normPartsCount[batch] <= 0) continue;
|
||||
if (hasNormParts[batch] || checkNormParts[batch]) {
|
||||
Gfx_BindTexture(Atlas1D_TexIds[batch]);
|
||||
MapRenderer_RenderNormalBatch(batch);
|
||||
MapRenderer_CheckingNormalParts[batch] = false;
|
||||
checkNormParts[batch] = false;
|
||||
}
|
||||
}
|
||||
Gfx_DisableMipmaps();
|
||||
|
@ -160,13 +203,13 @@ static void MapRenderer_RenderTranslucentBatch(int batch) {
|
|||
bool drawMin, drawMax;
|
||||
int i, offset;
|
||||
|
||||
for (i = 0; i < MapRenderer_RenderChunksCount; i++) {
|
||||
info = MapRenderer_RenderChunks[i];
|
||||
for (i = 0; i < renderChunksCount; i++) {
|
||||
info = renderChunks[i];
|
||||
if (!info->TranslucentParts) continue;
|
||||
|
||||
part = info->TranslucentParts[batchOffset];
|
||||
if (part.Offset < 0) continue;
|
||||
MapRenderer_HasTranslucentParts[batch] = true;
|
||||
hasTranParts[batch] = true;
|
||||
|
||||
#ifndef CC_BUILD_GL11
|
||||
Gfx_BindVb(info->Vb);
|
||||
|
@ -193,7 +236,7 @@ static void MapRenderer_RenderTranslucentBatch(int batch) {
|
|||
|
||||
void MapRenderer_RenderTranslucent(double delta) {
|
||||
int vertices, batch;
|
||||
if (!MapRenderer_Chunks) return;
|
||||
if (!mapChunks) return;
|
||||
|
||||
/* First fill depth buffer */
|
||||
vertices = Game_Vertices;
|
||||
|
@ -203,10 +246,10 @@ void MapRenderer_RenderTranslucent(double delta) {
|
|||
Gfx_SetColWriteMask(false, false, false, false);
|
||||
|
||||
for (batch = 0; batch < MapRenderer_1DUsedCount; batch++) {
|
||||
if (MapRenderer_TranslucentPartsCount[batch] <= 0) continue;
|
||||
if (MapRenderer_HasTranslucentParts[batch] || MapRenderer_CheckingTranslucentParts[batch]) {
|
||||
if (tranPartsCount[batch] <= 0) continue;
|
||||
if (hasTranParts[batch] || checkTranParts[batch]) {
|
||||
MapRenderer_RenderTranslucentBatch(batch);
|
||||
MapRenderer_CheckingTranslucentParts[batch] = false;
|
||||
checkTranParts[batch] = false;
|
||||
}
|
||||
}
|
||||
Game_Vertices = vertices;
|
||||
|
@ -219,8 +262,8 @@ void MapRenderer_RenderTranslucent(double delta) {
|
|||
|
||||
Gfx_EnableMipmaps();
|
||||
for (batch = 0; batch < MapRenderer_1DUsedCount; batch++) {
|
||||
if (MapRenderer_TranslucentPartsCount[batch] <= 0) continue;
|
||||
if (!MapRenderer_HasTranslucentParts[batch]) continue;
|
||||
if (tranPartsCount[batch] <= 0) continue;
|
||||
if (!hasTranParts[batch]) continue;
|
||||
Gfx_BindTexture(Atlas1D_TexIds[batch]);
|
||||
MapRenderer_RenderTranslucentBatch(batch);
|
||||
}
|
||||
|
@ -236,3 +279,498 @@ void MapRenderer_RenderTranslucent(double delta) {
|
|||
Gfx_SetAlphaBlending(false);
|
||||
Gfx_SetTexturing(false);
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*----------------------------------------------------Chunks mangagement---------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
static void MapRenderer_FreeParts(void) {
|
||||
Mem_Free(partsBuffer_Raw);
|
||||
partsBuffer_Raw = NULL;
|
||||
MapRenderer_PartsNormal = NULL;
|
||||
MapRenderer_PartsTranslucent = NULL;
|
||||
}
|
||||
|
||||
static void MapRenderer_FreeChunks(void) {
|
||||
Mem_Free(mapChunks);
|
||||
Mem_Free(sortedChunks);
|
||||
Mem_Free(renderChunks);
|
||||
Mem_Free(distances);
|
||||
|
||||
mapChunks = NULL;
|
||||
sortedChunks = NULL;
|
||||
renderChunks = NULL;
|
||||
distances = NULL;
|
||||
}
|
||||
|
||||
static void MapRenderer_AllocateParts(void) {
|
||||
uint32_t count = MapRenderer_ChunksCount * MapRenderer_1DUsedCount;
|
||||
partsBuffer_Raw = Mem_AllocCleared(count * 2, sizeof(struct ChunkPartInfo), "chunk parts");
|
||||
MapRenderer_PartsNormal = partsBuffer_Raw;
|
||||
MapRenderer_PartsTranslucent = partsBuffer_Raw + count;
|
||||
}
|
||||
|
||||
static void MapRenderer_AllocateChunks(void) {
|
||||
mapChunks = Mem_Alloc(MapRenderer_ChunksCount, sizeof(struct ChunkInfo), "chunk info");
|
||||
sortedChunks = Mem_Alloc(MapRenderer_ChunksCount, sizeof(struct ChunkInfo*), "sorted chunk info");
|
||||
renderChunks = Mem_Alloc(MapRenderer_ChunksCount, sizeof(struct ChunkInfo*), "render chunk info");
|
||||
distances = Mem_Alloc(MapRenderer_ChunksCount, 4, "chunk distances");
|
||||
}
|
||||
|
||||
static void MapRenderer_ResetPartFlags(void) {
|
||||
int i;
|
||||
for (i = 0; i < ATLAS1D_MAX_ATLASES; i++) {
|
||||
checkNormParts[i] = true;
|
||||
hasNormParts[i] = false;
|
||||
checkTranParts[i] = true;
|
||||
hasTranParts[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_ResetPartCounts(void) {
|
||||
int i;
|
||||
for (i = 0; i < ATLAS1D_MAX_ATLASES; i++) {
|
||||
normPartsCount[i] = 0;
|
||||
tranPartsCount[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_InitChunks(void) {
|
||||
int x, y, z, index = 0;
|
||||
for (z = 0; z < World_Length; z += CHUNK_SIZE) {
|
||||
for (y = 0; y < World_Height; y += CHUNK_SIZE) {
|
||||
for (x = 0; x < World_Width; x += CHUNK_SIZE) {
|
||||
ChunkInfo_Reset(&mapChunks[index], x, y, z);
|
||||
sortedChunks[index] = &mapChunks[index];
|
||||
renderChunks[index] = &mapChunks[index];
|
||||
distances[index] = 0;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_ResetChunks(void) {
|
||||
int x, y, z, index = 0;
|
||||
for (z = 0; z < World_Length; z += CHUNK_SIZE) {
|
||||
for (y = 0; y < World_Height; y += CHUNK_SIZE) {
|
||||
for (x = 0; x < World_Width; x += CHUNK_SIZE) {
|
||||
ChunkInfo_Reset(&mapChunks[index], x, y, z);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_DeleteChunks(void) {
|
||||
int i;
|
||||
if (!mapChunks) return;
|
||||
|
||||
for (i = 0; i < MapRenderer_ChunksCount; i++) {
|
||||
MapRenderer_DeleteChunk(&mapChunks[i]);
|
||||
}
|
||||
MapRenderer_ResetPartCounts();
|
||||
}
|
||||
|
||||
void MapRenderer_Refresh(void) {
|
||||
int oldCount;
|
||||
chunkPos = Vector3I_MaxValue();
|
||||
|
||||
if (mapChunks && World_Blocks) {
|
||||
MapRenderer_DeleteChunks();
|
||||
MapRenderer_ResetChunks();
|
||||
|
||||
oldCount = MapRenderer_1DUsedCount;
|
||||
MapRenderer_1DUsedCount = Atlas1D_UsedAtlasesCount();
|
||||
/* Need to reallocate parts array in this case */
|
||||
if (MapRenderer_1DUsedCount != oldCount) {
|
||||
MapRenderer_FreeParts();
|
||||
MapRenderer_AllocateParts();
|
||||
}
|
||||
}
|
||||
MapRenderer_ResetPartCounts();
|
||||
}
|
||||
|
||||
void MapRenderer_RefreshBorders(int maxHeight) {
|
||||
int cx, cy, cz;
|
||||
bool onBorder;
|
||||
|
||||
chunkPos = Vector3I_MaxValue();
|
||||
if (!mapChunks || !World_Blocks) return;
|
||||
|
||||
for (cz = 0; cz < MapRenderer_ChunksZ; cz++) {
|
||||
for (cy = 0; cy < MapRenderer_ChunksY; cy++) {
|
||||
for (cx = 0; cx < MapRenderer_ChunksX; cx++) {
|
||||
onBorder = cx == 0 || cz == 0 || cx == (MapRenderer_ChunksX - 1) || cz == (MapRenderer_ChunksZ - 1);
|
||||
|
||||
if (onBorder && (cy * CHUNK_SIZE) < maxHeight) {
|
||||
MapRenderer_RefreshChunk(cx, cy, cz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*--------------------------------------------------Chunks updating/sorting------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
#define CHUNK_TARGET_TIME ((1.0/30) + 0.01)
|
||||
static int chunksTarget = 12;
|
||||
static Vector3 lastCamPos;
|
||||
static float lastHeadY, lastHeadX;
|
||||
|
||||
static int MapRenderer_AdjustViewDist(int dist) {
|
||||
if (dist < CHUNK_SIZE) dist = CHUNK_SIZE;
|
||||
dist = Utils_AdjViewDist(dist);
|
||||
return (dist + 24) * (dist + 24);
|
||||
}
|
||||
|
||||
static int MapRenderer_UpdateChunksAndVisibility(int* chunkUpdates) {
|
||||
int viewDistSqr = MapRenderer_AdjustViewDist(Game_ViewDistance);
|
||||
int userDistSqr = MapRenderer_AdjustViewDist(Game_UserViewDistance);
|
||||
|
||||
struct ChunkInfo* info;
|
||||
int i, j = 0, distSqr;
|
||||
bool noData;
|
||||
|
||||
for (i = 0; i < MapRenderer_ChunksCount; i++) {
|
||||
info = sortedChunks[i];
|
||||
if (info->Empty) continue;
|
||||
|
||||
distSqr = distances[i];
|
||||
noData = !info->NormalParts && !info->TranslucentParts;
|
||||
|
||||
/* Unload chunks beyond visible range */
|
||||
if (!noData && distSqr >= userDistSqr + 32 * 16) {
|
||||
MapRenderer_DeleteChunk(info); continue;
|
||||
}
|
||||
noData |= info->PendingDelete;
|
||||
|
||||
if (noData && distSqr <= viewDistSqr && *chunkUpdates < chunksTarget) {
|
||||
MapRenderer_DeleteChunk(info);
|
||||
MapRenderer_BuildChunk(info, chunkUpdates);
|
||||
}
|
||||
|
||||
info->Visible = distSqr <= viewDistSqr &&
|
||||
FrustumCulling_SphereInFrustum(info->CentreX, info->CentreY, info->CentreZ, 14); /* 14 ~ sqrt(3 * 8^2) */
|
||||
if (info->Visible && !info->Empty) { renderChunks[j] = info; j++; }
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
static int MapRenderer_UpdateChunksStill(int* chunkUpdates) {
|
||||
int viewDistSqr = MapRenderer_AdjustViewDist(Game_ViewDistance);
|
||||
int userDistSqr = MapRenderer_AdjustViewDist(Game_UserViewDistance);
|
||||
|
||||
struct ChunkInfo* info;
|
||||
int i, j = 0, distSqr;
|
||||
bool noData;
|
||||
|
||||
for (i = 0; i < MapRenderer_ChunksCount; i++) {
|
||||
info = sortedChunks[i];
|
||||
if (info->Empty) continue;
|
||||
|
||||
distSqr = distances[i];
|
||||
noData = !info->NormalParts && !info->TranslucentParts;
|
||||
|
||||
/* Unload chunks beyond visible range */
|
||||
if (!noData && distSqr >= userDistSqr + 32 * 16) {
|
||||
MapRenderer_DeleteChunk(info); continue;
|
||||
}
|
||||
noData |= info->PendingDelete;
|
||||
|
||||
if (noData && distSqr <= userDistSqr && *chunkUpdates < chunksTarget) {
|
||||
MapRenderer_DeleteChunk(info);
|
||||
MapRenderer_BuildChunk(info, chunkUpdates);
|
||||
|
||||
/* only need to update the visibility of chunks in range. */
|
||||
info->Visible = distSqr <= viewDistSqr &&
|
||||
FrustumCulling_SphereInFrustum(info->CentreX, info->CentreY, info->CentreZ, 14); /* 14 ~ sqrt(3 * 8^2) */
|
||||
if (info->Visible && !info->Empty) { renderChunks[j] = info; j++; }
|
||||
} else if (info->Visible) {
|
||||
renderChunks[j] = info; j++;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
static void MapRenderer_UpdateChunks(double delta) {
|
||||
struct LocalPlayer* p;
|
||||
bool samePos;
|
||||
int chunkUpdates = 0;
|
||||
|
||||
/* Build more chunks if 30 FPS or over, otherwise slowdown */
|
||||
chunksTarget += delta < CHUNK_TARGET_TIME ? 1 : -1;
|
||||
Math_Clamp(chunksTarget, 4, Game_MaxChunkUpdates);
|
||||
|
||||
p = &LocalPlayer_Instance;
|
||||
samePos = Vector3_Equals(&Camera_CurrentPos, &lastCamPos)
|
||||
&& p->Base.HeadX == lastHeadX && p->Base.HeadY == lastHeadY;
|
||||
|
||||
renderChunksCount = samePos ?
|
||||
MapRenderer_UpdateChunksStill(&chunkUpdates) :
|
||||
MapRenderer_UpdateChunksAndVisibility(&chunkUpdates);
|
||||
|
||||
lastCamPos = Camera_CurrentPos;
|
||||
lastHeadX = p->Base.HeadX;
|
||||
lastHeadY = p->Base.HeadY;
|
||||
|
||||
if (!samePos || chunkUpdates) {
|
||||
MapRenderer_ResetPartFlags();
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_QuickSort(int left, int right) {
|
||||
struct ChunkInfo** values = sortedChunks; struct ChunkInfo* value;
|
||||
uint32_t* keys = distances; uint32_t key;
|
||||
|
||||
while (left < right) {
|
||||
int i = left, j = right;
|
||||
uint32_t pivot = keys[(i + j) >> 1];
|
||||
|
||||
/* partition the list */
|
||||
while (i <= j) {
|
||||
while (pivot > keys[i]) i++;
|
||||
while (pivot < keys[j]) j--;
|
||||
QuickSort_Swap_KV_Maybe();
|
||||
}
|
||||
/* recurse into the smaller subset */
|
||||
QuickSort_Recurse(MapRenderer_QuickSort)
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_UpdateSortOrder(void) {
|
||||
struct ChunkInfo* info;
|
||||
Vector3I pos;
|
||||
int dXMin, dXMax, dYMin, dYMax, dZMin, dZMax;
|
||||
int i, dx, dy, dz;
|
||||
|
||||
/* pos is centre coordinate of chunk camera is in */
|
||||
Vector3I_Floor(&pos, &Camera_CurrentPos);
|
||||
pos.X = (pos.X & ~CHUNK_MASK) + HALF_CHUNK_SIZE;
|
||||
pos.Y = (pos.Y & ~CHUNK_MASK) + HALF_CHUNK_SIZE;
|
||||
pos.Z = (pos.Z & ~CHUNK_MASK) + HALF_CHUNK_SIZE;
|
||||
|
||||
/* If in same chunk, don't need to recalculate sort order */
|
||||
if (Vector3I_Equals(&pos, &chunkPos)) return;
|
||||
chunkPos = pos;
|
||||
if (!MapRenderer_ChunksCount) return;
|
||||
|
||||
for (i = 0; i < MapRenderer_ChunksCount; i++) {
|
||||
info = sortedChunks[i];
|
||||
/* Calculate distance to chunk centre */
|
||||
dx = info->CentreX - pos.X; dy = info->CentreY - pos.Y; dz = info->CentreZ - pos.Z;
|
||||
distances[i] = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
/* Can work out distance to chunk faces as offset from distance to chunk centre on each axis */
|
||||
dXMin = dx - HALF_CHUNK_SIZE; dXMax = dx + HALF_CHUNK_SIZE;
|
||||
dYMin = dy - HALF_CHUNK_SIZE; dYMax = dy + HALF_CHUNK_SIZE;
|
||||
dZMin = dz - HALF_CHUNK_SIZE; dZMax = dz + HALF_CHUNK_SIZE;
|
||||
|
||||
/* Back face culling: make sure that the chunk is definitely entirely back facing */
|
||||
info->DrawXMin = !(dXMin <= 0 && dXMax <= 0);
|
||||
info->DrawXMax = !(dXMin >= 0 && dXMax >= 0);
|
||||
info->DrawZMin = !(dZMin <= 0 && dZMax <= 0);
|
||||
info->DrawZMax = !(dZMin >= 0 && dZMax >= 0);
|
||||
info->DrawYMin = !(dYMin <= 0 && dYMax <= 0);
|
||||
info->DrawYMax = !(dYMin >= 0 && dYMax >= 0);
|
||||
}
|
||||
|
||||
MapRenderer_QuickSort(0, MapRenderer_ChunksCount - 1);
|
||||
MapRenderer_ResetPartFlags();
|
||||
/*SimpleOcclusionCulling();*/
|
||||
}
|
||||
|
||||
void MapRenderer_Update(double deltaTime) {
|
||||
if (!mapChunks) return;
|
||||
MapRenderer_UpdateSortOrder();
|
||||
MapRenderer_UpdateChunks(deltaTime);
|
||||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*---------------------------------------------------------General---------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
void MapRenderer_RefreshChunk(int cx, int cy, int cz) {
|
||||
struct ChunkInfo* info;
|
||||
if (cx < 0 || cy < 0 || cz < 0 || cx >= MapRenderer_ChunksX
|
||||
|| cy >= MapRenderer_ChunksY || cz >= MapRenderer_ChunksZ) return;
|
||||
|
||||
info = &mapChunks[MapRenderer_Pack(cx, cy, cz)];
|
||||
if (info->AllAir) return; /* do not recreate chunks completely air */
|
||||
info->Empty = false;
|
||||
info->PendingDelete = true;
|
||||
}
|
||||
|
||||
void MapRenderer_DeleteChunk(struct ChunkInfo* info) {
|
||||
struct ChunkPartInfo* ptr;
|
||||
int i;
|
||||
|
||||
info->Empty = false; info->AllAir = false;
|
||||
#ifdef OCCLUSION
|
||||
info.OcclusionFlags = 0;
|
||||
info.OccludedFlags = 0;
|
||||
#endif
|
||||
#ifndef CC_BUILD_GL11
|
||||
Gfx_DeleteVb(&info->Vb);
|
||||
#endif
|
||||
|
||||
if (info->NormalParts) {
|
||||
ptr = info->NormalParts;
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += MapRenderer_ChunksCount) {
|
||||
if (ptr->Offset < 0) continue;
|
||||
normPartsCount[i]--;
|
||||
#ifdef CC_BUILD_GL11
|
||||
Gfx_DeleteVb(&ptr->Vb);
|
||||
#endif
|
||||
}
|
||||
info->NormalParts = NULL;
|
||||
}
|
||||
|
||||
if (info->TranslucentParts) {
|
||||
ptr = info->TranslucentParts;
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += MapRenderer_ChunksCount) {
|
||||
if (ptr->Offset < 0) continue;
|
||||
tranPartsCount[i]--;
|
||||
#ifdef CC_BUILD_GL11
|
||||
Gfx_DeleteVb(&ptr->Vb);
|
||||
#endif
|
||||
}
|
||||
info->TranslucentParts = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void MapRenderer_BuildChunk(struct ChunkInfo* info, int* chunkUpdates) {
|
||||
struct ChunkPartInfo* ptr;
|
||||
int i;
|
||||
|
||||
Game_ChunkUpdates++;
|
||||
(*chunkUpdates)++;
|
||||
info->PendingDelete = false;
|
||||
Builder_MakeChunk(info);
|
||||
|
||||
if (!info->NormalParts && !info->TranslucentParts) {
|
||||
info->Empty = true; return;
|
||||
}
|
||||
|
||||
if (info->NormalParts) {
|
||||
ptr = info->NormalParts;
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += MapRenderer_ChunksCount) {
|
||||
if (ptr->Offset >= 0) normPartsCount[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->TranslucentParts) {
|
||||
ptr = info->TranslucentParts;
|
||||
for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += MapRenderer_ChunksCount) {
|
||||
if (ptr->Offset >= 0) tranPartsCount[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_EnvVariableChanged(void* obj, int envVar) {
|
||||
if (envVar == ENV_VAR_SUN_COL || envVar == ENV_VAR_SHADOW_COL) {
|
||||
MapRenderer_Refresh();
|
||||
} else if (envVar == ENV_VAR_EDGE_HEIGHT || envVar == ENV_VAR_SIDES_OFFSET) {
|
||||
int oldClip = Builder_EdgeLevel;
|
||||
Builder_SidesLevel = max(0, Env_SidesHeight);
|
||||
Builder_EdgeLevel = max(0, Env_EdgeHeight);
|
||||
|
||||
/* Only need to refresh chunks on map borders up to highest edge level.*/
|
||||
MapRenderer_RefreshBorders(max(oldClip, Builder_EdgeLevel));
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_TerrainAtlasChanged(void* obj) {
|
||||
if (MapRenderer_1DUsedCount) {
|
||||
bool refreshRequired = elementsPerBitmap != Atlas1D_TilesPerAtlas;
|
||||
if (refreshRequired) MapRenderer_Refresh();
|
||||
}
|
||||
|
||||
MapRenderer_1DUsedCount = Atlas1D_UsedAtlasesCount();
|
||||
elementsPerBitmap = Atlas1D_TilesPerAtlas;
|
||||
MapRenderer_ResetPartFlags();
|
||||
}
|
||||
|
||||
static void MapRenderer_BlockDefinitionChanged(void* obj) {
|
||||
MapRenderer_Refresh();
|
||||
MapRenderer_1DUsedCount = Atlas1D_UsedAtlasesCount();
|
||||
MapRenderer_ResetPartFlags();
|
||||
}
|
||||
|
||||
static void MapRenderer_RecalcVisibility_(void* obj) { lastCamPos = Vector3_BigPos(); }
|
||||
static void MapRenderer_DeleteChunks_(void* obj) { MapRenderer_DeleteChunks(); }
|
||||
static void MapRenderer_Refresh_(void* obj) { MapRenderer_Refresh(); }
|
||||
|
||||
void MapRenderer_ApplyMeshBuilder(void) {
|
||||
if (Game_SmoothLighting) {
|
||||
AdvBuilder_SetActive();
|
||||
} else {
|
||||
NormalBuilder_SetActive();
|
||||
}
|
||||
}
|
||||
|
||||
static void MapRenderer_OnNewMap(void* obj) {
|
||||
Game_ChunkUpdates = 0;
|
||||
MapRenderer_DeleteChunks();
|
||||
MapRenderer_ResetPartCounts();
|
||||
|
||||
chunkPos = Vector3I_MaxValue();
|
||||
MapRenderer_FreeChunks();
|
||||
MapRenderer_FreeParts();
|
||||
}
|
||||
|
||||
static void MapRenderer_OnNewMapLoaded(void* obj) {
|
||||
int count;
|
||||
MapRenderer_ChunksX = (World_Width + CHUNK_MAX) >> CHUNK_SHIFT;
|
||||
MapRenderer_ChunksY = (World_Height + CHUNK_MAX) >> CHUNK_SHIFT;
|
||||
MapRenderer_ChunksZ = (World_Length + CHUNK_MAX) >> CHUNK_SHIFT;
|
||||
|
||||
count = MapRenderer_ChunksX * MapRenderer_ChunksY * MapRenderer_ChunksZ;
|
||||
/* TODO: Only perform reallocation when map volume has changed */
|
||||
/*if (MapRenderer_ChunksCount != count) { */
|
||||
MapRenderer_ChunksCount = count;
|
||||
MapRenderer_FreeChunks();
|
||||
MapRenderer_FreeParts();
|
||||
MapRenderer_AllocateChunks();
|
||||
MapRenderer_AllocateParts();
|
||||
/*}*/
|
||||
|
||||
MapRenderer_InitChunks();
|
||||
Builder_OnNewMapLoaded();
|
||||
lastCamPos = Vector3_BigPos();
|
||||
}
|
||||
|
||||
void MapRenderer_Init(void) {
|
||||
Event_RegisterVoid(&TextureEvents_AtlasChanged, NULL, MapRenderer_TerrainAtlasChanged);
|
||||
Event_RegisterVoid(&WorldEvents_NewMap, NULL, MapRenderer_OnNewMap);
|
||||
Event_RegisterVoid(&WorldEvents_MapLoaded, NULL, MapRenderer_OnNewMapLoaded);
|
||||
Event_RegisterInt(&WorldEvents_EnvVarChanged, NULL, MapRenderer_EnvVariableChanged);
|
||||
|
||||
Event_RegisterVoid(&BlockEvents_BlockDefChanged, NULL, MapRenderer_BlockDefinitionChanged);
|
||||
Event_RegisterVoid(&GfxEvents_ViewDistanceChanged, NULL, MapRenderer_RecalcVisibility_);
|
||||
Event_RegisterVoid(&GfxEvents_ProjectionChanged, NULL, MapRenderer_RecalcVisibility_);
|
||||
Event_RegisterVoid(&GfxEvents_ContextLost, NULL, MapRenderer_DeleteChunks_);
|
||||
Event_RegisterVoid(&GfxEvents_ContextRecreated, NULL, MapRenderer_Refresh_);
|
||||
|
||||
/* This = 87 fixes map being invisible when no textures */
|
||||
MapRenderer_1DUsedCount = 87; /* Atlas1D_UsedAtlasesCount(); */
|
||||
chunkPos = Vector3I_MaxValue();
|
||||
MapRenderer_ApplyMeshBuilder();
|
||||
}
|
||||
|
||||
void MapRenderer_Free(void) {
|
||||
Event_UnregisterVoid(&TextureEvents_AtlasChanged, NULL, MapRenderer_TerrainAtlasChanged);
|
||||
Event_UnregisterVoid(&WorldEvents_NewMap, NULL, MapRenderer_OnNewMap);
|
||||
Event_UnregisterVoid(&WorldEvents_MapLoaded, NULL, MapRenderer_OnNewMapLoaded);
|
||||
Event_UnregisterInt(&WorldEvents_EnvVarChanged, NULL, MapRenderer_EnvVariableChanged);
|
||||
|
||||
Event_UnregisterVoid(&BlockEvents_BlockDefChanged, NULL, MapRenderer_BlockDefinitionChanged);
|
||||
Event_UnregisterVoid(&GfxEvents_ViewDistanceChanged, NULL, MapRenderer_RecalcVisibility_);
|
||||
Event_UnregisterVoid(&GfxEvents_ProjectionChanged, NULL, MapRenderer_RecalcVisibility_);
|
||||
Event_UnregisterVoid(&GfxEvents_ContextLost, NULL, MapRenderer_DeleteChunks_);
|
||||
Event_UnregisterVoid(&GfxEvents_ContextRecreated, NULL, MapRenderer_Refresh_);
|
||||
|
||||
MapRenderer_OnNewMap(NULL);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#ifndef CC_MAPRENDERER_H
|
||||
#define CC_MAPRENDERER_H
|
||||
#include "TerrainAtlas.h"
|
||||
#include "ChunkUpdater.h"
|
||||
#include "Core.h"
|
||||
#include "Constants.h"
|
||||
/* Renders the blocks of the world by subdividing it into chunks.
|
||||
Also manages the process of building/deleting chunk meshes.
|
||||
Also sorts chunks so nearest chunks are rendered first, and calculates chunk visibility.
|
||||
Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
*/
|
||||
|
||||
|
@ -10,45 +12,84 @@ int MapRenderer_ChunksX, MapRenderer_ChunksY, MapRenderer_ChunksZ;
|
|||
#define MapRenderer_Pack(cx, cy, cz) (((cz) * MapRenderer_ChunksY + (cy)) * MapRenderer_ChunksX + (cx))
|
||||
/* TODO: Swap Y and Z? Make sure to update ChunkUpdater's ResetChunkCache and ClearChunkCache methods! */
|
||||
|
||||
/* The count of actual used 1D atlases. (i.e. 1DIndex(maxTextureLoc) + 1 */
|
||||
/* Count of actual used 1D atlases. (i.e. 1DIndex(maxTextureLoc) + 1 */
|
||||
int MapRenderer_1DUsedCount;
|
||||
/* The number of non-empty Normal ChunkPartInfos (across entire world) for each 1D atlas batch.
|
||||
1D atlas batches that do not have any ChunkPartInfos can be entirely skipped. */
|
||||
int MapRenderer_NormalPartsCount[ATLAS1D_MAX_ATLASES];
|
||||
/* The number of non-empty Translucent ChunkPartInfos (across entire world) for each 1D atlas batch.
|
||||
1D atlas batches that do not have any ChunkPartInfos can be entirely skipped. */
|
||||
int MapRenderer_TranslucentPartsCount[ATLAS1D_MAX_ATLASES];
|
||||
/* Whether there are any visible Translucent ChunkPartInfos for each 1D atlas batch.
|
||||
1D atlas batches that do not have any visible translucent ChunkPartInfos can be skipped. */
|
||||
bool MapRenderer_HasTranslucentParts[ATLAS1D_MAX_ATLASES];
|
||||
/* Whether there are any visible Normal ChunkPartInfos for each 1D atlas batch.
|
||||
1D atlas batches that do not have any visible normal ChunkPartInfos can be skipped. */
|
||||
bool MapRenderer_HasNormalParts[ATLAS1D_MAX_ATLASES];
|
||||
/* Whether renderer should check if there are any visible Translucent ChunkPartInfos for each 1D atlas batch. */
|
||||
bool MapRenderer_CheckingTranslucentParts[ATLAS1D_MAX_ATLASES];
|
||||
/* Whether renderer should check if there are any visible Normal ChunkPartInfos for each 1D atlas batch. */
|
||||
bool MapRenderer_CheckingNormalParts[ATLAS1D_MAX_ATLASES];
|
||||
|
||||
/* Render info for all chunks in the world. Unsorted.*/
|
||||
struct ChunkInfo* MapRenderer_Chunks;
|
||||
/* The number of chunks in the world, or ChunksX * ChunksY * ChunksZ */
|
||||
/* Number of chunks in the world, or ChunksX * ChunksY * ChunksZ */
|
||||
int MapRenderer_ChunksCount;
|
||||
/* Pointers to render info for all chunks in the world, sorted by distance from the camera. */
|
||||
struct ChunkInfo** MapRenderer_SortedChunks;
|
||||
/* Pointers to render info for all chunks in the world, sorted by distance from the camera.
|
||||
Chunks that can be rendered (not empty and are visible) are included in this array. */
|
||||
struct ChunkInfo** MapRenderer_RenderChunks;
|
||||
/* The number of actually used pointers in the RenderChunks array.
|
||||
Entries past this count should be ignored and skipped. */
|
||||
int MapRenderer_RenderChunksCount;
|
||||
/* Buffer for all chunk parts. There are (MapRenderer_ChunksCount * Atlas1D_Count) * 2 parts in the buffer,
|
||||
with parts for 'normal' buffer being in lower half. */
|
||||
struct ChunkPartInfo* MapRenderer_PartsBuffer_Raw;
|
||||
struct ChunkPartInfo* MapRenderer_PartsNormal;
|
||||
|
||||
/* Buffer for all chunk parts. There are (MapRenderer_ChunksCount * Atlas1D_Count) parts in the buffer,
|
||||
with parts for 'normal' buffer being in lower half. */
|
||||
struct ChunkPartInfo* MapRenderer_PartsNormal; /* TODO: THAT DESC SUCKS */
|
||||
struct ChunkPartInfo* MapRenderer_PartsTranslucent;
|
||||
|
||||
struct ChunkInfo* MapRenderer_GetChunk(int cx, int cy, int cz);
|
||||
void MapRenderer_RefreshChunk(int cx, int cy, int cz);
|
||||
void MapRenderer_RenderNormal(double delta);
|
||||
void MapRenderer_RenderTranslucent(double delta);
|
||||
/* Describes a portion of the data needed for rendering a chunk. */
|
||||
struct ChunkPartInfo {
|
||||
#ifdef CC_BUILD_GL11
|
||||
GfxResourceID Vb;
|
||||
#endif
|
||||
int Offset; /* -1 if no vertices at all */
|
||||
int SpriteCount; /* Sprite vertices count */
|
||||
uint16_t Counts[FACE_COUNT]; /* Counts per face */
|
||||
};
|
||||
|
||||
/* Describes data necessary for rendering a chunk. */
|
||||
struct ChunkInfo {
|
||||
uint16_t CentreX, CentreY, CentreZ; /* Centre coordinates of the chunk */
|
||||
|
||||
uint8_t Visible : 1; /* Whether chunk is visibile to the player */
|
||||
uint8_t Empty : 1; /* Whether the chunk is empty of data */
|
||||
uint8_t PendingDelete : 1; /* Whether chunk is pending deletion*/
|
||||
uint8_t AllAir : 1; /* Whether chunk is completely air */
|
||||
uint8_t : 0; /* pad to next byte*/
|
||||
|
||||
uint8_t DrawXMin : 1;
|
||||
uint8_t DrawXMax : 1;
|
||||
uint8_t DrawZMin : 1;
|
||||
uint8_t DrawZMax : 1;
|
||||
uint8_t DrawYMin : 1;
|
||||
uint8_t DrawYMax : 1;
|
||||
uint8_t : 0; /* pad to next byte */
|
||||
#ifdef OCCLUSION
|
||||
public bool Visited = false, Occluded = false;
|
||||
public byte OcclusionFlags, OccludedFlags, DistanceFlags;
|
||||
#endif
|
||||
#ifndef CC_BUILD_GL11
|
||||
GfxResourceID Vb;
|
||||
#endif
|
||||
struct ChunkPartInfo* NormalParts;
|
||||
struct ChunkPartInfo* TranslucentParts;
|
||||
};
|
||||
|
||||
void ChunkInfo_Reset(struct ChunkInfo* chunk, int x, int y, int z);
|
||||
/* Gets the chunk at the given chunk coordinates in the world. */
|
||||
/* NOTE: Does NOT check coordinates are within bounds. */
|
||||
struct ChunkInfo* MapRenderer_GetChunk(int cx, int cy, int cz);
|
||||
/* Renders the meshes of non-translucent blocks in visible chunks. */
|
||||
void MapRenderer_RenderNormal(double delta);
|
||||
/* Renders the meshes of translucent blocks in visible chunks. */
|
||||
void MapRenderer_RenderTranslucent(double delta);
|
||||
/* Potentially updates sort order of rendered chunks. */
|
||||
/* Potentially builds meshes for several nearby chunks. */
|
||||
/* NOTE: This should be called once per frame. */
|
||||
void MapRenderer_Update(double deltaTime);
|
||||
|
||||
/* Marks the given chunk as needing to be rebuilt/redrawn. */
|
||||
/* NOTE: Coordinates outside the map are simply ignored. */
|
||||
void MapRenderer_RefreshChunk(int cx, int cy, int cz);
|
||||
/* Deletes the vertex buffer associated with the given chunk. */
|
||||
/* NOTE: This method also adjusts internal state, so do not bypass this. */
|
||||
void MapRenderer_DeleteChunk(struct ChunkInfo* info);
|
||||
/* Builds the mesh (and hence vertex buffer) for the given chunk. */
|
||||
/* NOTE: This method also adjusts internal state, so do not bypass this. */
|
||||
void MapRenderer_BuildChunk(struct ChunkInfo* info, int* chunkUpdates);
|
||||
|
||||
/* Refreshes chunks on the border of the map. */
|
||||
/* NOTE: Only refreshes chunks whose y is less than 'maxHeight'. */
|
||||
void MapRenderer_RefreshBorders(int maxHeight);
|
||||
/* Deletes all chunks and resets internal state. */
|
||||
void MapRenderer_Refresh(void);
|
||||
|
||||
void MapRenderer_Init(void);
|
||||
void MapRenderer_Free(void);
|
||||
void MapRenderer_ApplyMeshBuilder(void);
|
||||
#endif
|
||||
|
|
|
@ -2393,8 +2393,8 @@ static void GraphicsOptionsScreen_SetViewDist(const String* v) { Game_UserSetVie
|
|||
static void GraphicsOptionsScreen_GetSmooth(String* v) { Menu_GetBool(v, Game_SmoothLighting); }
|
||||
static void GraphicsOptionsScreen_SetSmooth(const String* v) {
|
||||
Game_SmoothLighting = Menu_SetBool(v, OPT_SMOOTH_LIGHTING);
|
||||
ChunkUpdater_ApplyMeshBuilder();
|
||||
ChunkUpdater_Refresh();
|
||||
MapRenderer_ApplyMeshBuilder();
|
||||
MapRenderer_Refresh();
|
||||
}
|
||||
|
||||
static void GraphicsOptionsScreen_GetNames(String* v) { String_AppendConst(v, NameMode_Names[Entities_NameMode]); }
|
||||
|
|
|
@ -75,7 +75,7 @@ CC_NOINLINE bool String_CaselessEquals(const String* a, const String* b);
|
|||
CC_NOINLINE bool String_CaselessEqualsConst(const String* a, const char* b);
|
||||
/* Breaks down an integer into an array of digits. */
|
||||
/* NOTE: Digits are in reverse order, so e.g. '200' becomes '0','0','2' */
|
||||
CC_NOINLINE int String_MakeUInt32(uint32_t num, char* numBuffer);
|
||||
CC_NOINLINE int String_MakeUInt32(uint32_t num, char* digits);
|
||||
|
||||
/* Appends a character to the end of a string. */
|
||||
/* Returns false when str->length == str->capcity, true otherwise. */
|
||||
|
|
|
@ -989,7 +989,7 @@ int Window_GetWindowState(void) {
|
|||
return WINDOW_STATE_NORMAL;
|
||||
}
|
||||
|
||||
void Window_SendNetWMState(long op, Atom a1, Atom a2) {
|
||||
static void Window_SendNetWMState(long op, Atom a1, Atom a2) {
|
||||
XEvent ev = { 0 };
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.send_event = true;
|
||||
|
@ -1087,7 +1087,7 @@ void Window_Destroy(void) {
|
|||
Window_Exists = false;
|
||||
}
|
||||
|
||||
void Window_ToggleKey(XKeyEvent* keyEvent, bool pressed) {
|
||||
static void Window_ToggleKey(XKeyEvent* keyEvent, bool pressed) {
|
||||
KeySym keysym1 = XLookupKeysym(keyEvent, 0);
|
||||
KeySym keysym2 = XLookupKeysym(keyEvent, 1);
|
||||
|
||||
|
@ -1096,7 +1096,7 @@ void Window_ToggleKey(XKeyEvent* keyEvent, bool pressed) {
|
|||
if (key != KEY_NONE) Key_SetPressed(key, pressed);
|
||||
}
|
||||
|
||||
Atom Window_GetSelectionProperty(XEvent* e) {
|
||||
static Atom Window_GetSelectionProperty(XEvent* e) {
|
||||
Atom prop = e->xselectionrequest.property;
|
||||
if (prop) return prop;
|
||||
|
||||
|
@ -1104,7 +1104,7 @@ Atom Window_GetSelectionProperty(XEvent* e) {
|
|||
return e->xselectionrequest.target;
|
||||
}
|
||||
|
||||
bool Window_GetPendingEvent(XEvent* e) {
|
||||
static bool Window_GetPendingEvent(XEvent* e) {
|
||||
return XCheckWindowEvent(win_display, win_handle, win_eventMask, e) ||
|
||||
XCheckTypedWindowEvent(win_display, win_handle, ClientMessage, e) ||
|
||||
XCheckTypedWindowEvent(win_display, win_handle, SelectionNotify, e) ||
|
||||
|
|
Loading…
Add table
Reference in a new issue