mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-16 11:06:06 -04:00
WIP remove lights when blocks update
This commit is contained in:
parent
0b597fbb3f
commit
1077948a13
@ -13,12 +13,17 @@
|
||||
#include "Options.h"
|
||||
#include "Queue.h"
|
||||
|
||||
static struct LightNode {
|
||||
IVec3 coords;
|
||||
cc_uint8 brightness;
|
||||
};
|
||||
|
||||
/*########################################################################################################################*
|
||||
*----------------------------------------------------Modern lighting------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
|
||||
static struct Queue lightQueue;
|
||||
static struct Queue unlightQueue;
|
||||
|
||||
/* A 16x16 palette of sun and block light colors. */
|
||||
/* It is indexed by a byte where the leftmost 4 bits represent sunlight level and the rightmost 4 bits represent blocklight level */
|
||||
@ -108,8 +113,8 @@ static void ModernLighting_AllocState(void) {
|
||||
|
||||
chunkLightingDataFlags = (cc_uint8*)Mem_AllocCleared(chunksCount, sizeof(cc_uint8), "light flags");
|
||||
chunkLightingData = (LightingChunk*)Mem_AllocCleared(chunksCount, sizeof(LightingChunk), "light chunks");
|
||||
Queue_Init(&lightQueue, sizeof(IVec3));
|
||||
|
||||
Queue_Init(&lightQueue, sizeof(struct LightNode));
|
||||
Queue_Init(&unlightQueue, sizeof(struct LightNode));
|
||||
}
|
||||
|
||||
static void ModernLighting_FreeState(void) {
|
||||
@ -130,6 +135,7 @@ static void ModernLighting_FreeState(void) {
|
||||
chunkLightingDataFlags = NULL;
|
||||
chunkLightingData = NULL;
|
||||
Queue_Clear(&lightQueue);
|
||||
Queue_Clear(&unlightQueue);
|
||||
}
|
||||
|
||||
/* Converts chunk x/y/z coordinates to the corresponding index in chunks array/list */
|
||||
@ -139,6 +145,7 @@ static void ModernLighting_FreeState(void) {
|
||||
/* Converts global x/y/z coordinates to the corresponding index in a chunk */
|
||||
#define GlobalCoordsToChunkCoordsIndex(x, y, z) (LocalCoordsToIndex(x & CHUNK_MASK, y & CHUNK_MASK, z & CHUNK_MASK))
|
||||
|
||||
/* Sets the light level at this cell. Does NOT check that the cell is in bounds. */
|
||||
static void SetBlocklight(cc_uint8 blockLight, int x, int y, int z, cc_bool sun, cc_bool refreshChunk) {
|
||||
cc_uint8 clearMask;
|
||||
cc_uint8 shift = sun ? MODERN_LIGHTING_SUN_SHIFT : 0;
|
||||
@ -163,7 +170,7 @@ static void SetBlocklight(cc_uint8 blockLight, int x, int y, int z, cc_bool sun,
|
||||
chunkLightingData[chunkIndex][localIndex] &= clearMask;
|
||||
chunkLightingData[chunkIndex][localIndex] |= blockLight << shift;
|
||||
|
||||
//refresh chunk if new value is different
|
||||
/* There is no reason to refresh current chunk as the builder does that automatically */
|
||||
if (prevValue != chunkLightingData[chunkIndex][localIndex]) {
|
||||
if (lx == CHUNK_MAX) MapRenderer_RefreshChunk(cx + 1, cy, cz);
|
||||
if (lx == 0) MapRenderer_RefreshChunk(cx - 1, cy, cz);
|
||||
@ -178,6 +185,7 @@ static void SetBlocklight(cc_uint8 blockLight, int x, int y, int z, cc_bool sun,
|
||||
chunkLightingData[chunkIndex][localIndex] |= blockLight << shift;
|
||||
}
|
||||
}
|
||||
/* Returns the light level at this cell. Does NOT check that the cell is in bounds. */
|
||||
static cc_uint8 GetBlocklight(int x, int y, int z, cc_bool sun) {
|
||||
int cx = x >> CHUNK_SHIFT, lx = x & CHUNK_MASK;
|
||||
int cy = y >> CHUNK_SHIFT, ly = y & CHUNK_MASK;
|
||||
@ -215,49 +223,50 @@ static cc_bool CanLightPass(BlockID thisBlock, Face face) {
|
||||
}
|
||||
|
||||
#define Light_TrySpreadInto(axis, AXIS, dir, limit, isSun, thisFace, thatFace) \
|
||||
if (curNode.axis dir limit && \
|
||||
if (ln.coords.axis dir ## = limit && \
|
||||
CanLightPass(thisBlock, FACE_ ## AXIS ## thisFace) && \
|
||||
CanLightPass(World_GetBlock(curNode.x, curNode.y, curNode.z), FACE_ ## AXIS ## thatFace) && \
|
||||
GetBlocklight(curNode.x, curNode.y, curNode.z, isSun) < curLight) { \
|
||||
SetBlocklight(curLight, curNode.x, curNode.y, curNode.z, isSun, refreshChunk); \
|
||||
Queue_Enqueue(&lightQueue, &curNode); \
|
||||
CanLightPass(World_GetBlock(ln.coords.x, ln.coords.y, ln.coords.z), FACE_ ## AXIS ## thatFace) && \
|
||||
GetBlocklight(ln.coords.x, ln.coords.y, ln.coords.z, isSun) < ln.brightness) { \
|
||||
Queue_Enqueue(&lightQueue, &ln); \
|
||||
} \
|
||||
|
||||
|
||||
static void CalcLight(cc_uint8 blockLight, int x, int y, int z, cc_bool isSun, cc_bool refreshChunk) {
|
||||
SetBlocklight(blockLight, x, y, z, isSun, refreshChunk);
|
||||
IVec3 entry = { x, y, z };
|
||||
Queue_Enqueue(&lightQueue, &entry);
|
||||
|
||||
static void FlushLightQueue(cc_bool isSun, cc_bool refreshChunk) {
|
||||
while (lightQueue.count > 0) {
|
||||
IVec3 curNode = *(IVec3*)(Queue_Dequeue(&lightQueue));
|
||||
cc_uint8 curLight = GetBlocklight(curNode.x, curNode.y, curNode.z, isSun);
|
||||
if (curLight <= 0) { return; }
|
||||
struct LightNode ln = *(struct LightNode*)(Queue_Dequeue(&lightQueue));
|
||||
|
||||
BlockID thisBlock = World_GetBlock(curNode.x, curNode.y, curNode.z);
|
||||
curLight--;
|
||||
if (curLight == 0) continue;
|
||||
cc_uint8 brightnessHere = GetBlocklight(ln.coords.x, ln.coords.y, ln.coords.z, isSun);
|
||||
|
||||
curNode.x--;
|
||||
Light_TrySpreadInto(x, X, >, 0, isSun, MAX, MIN)
|
||||
curNode.x += 2;
|
||||
Light_TrySpreadInto(x, X, <, World.MaxX, isSun, MIN, MAX)
|
||||
curNode.x--;
|
||||
/* If this cel is already just as or more lit, we can assume this cel and its neighbors have been accounted for */
|
||||
if (brightnessHere >= ln.brightness) { continue; }
|
||||
|
||||
curNode.y--;
|
||||
Light_TrySpreadInto(y, Y, >, 0, isSun, MAX, MIN)
|
||||
curNode.y += 2;
|
||||
Light_TrySpreadInto(y, Y, <, World.MaxY, isSun, MIN, MAX)
|
||||
curNode.y--;
|
||||
SetBlocklight(ln.brightness, ln.coords.x, ln.coords.y, ln.coords.z, isSun, refreshChunk);
|
||||
|
||||
curNode.z--;
|
||||
Light_TrySpreadInto(z, Z, >, 0, isSun, MAX, MIN)
|
||||
curNode.z += 2;
|
||||
Light_TrySpreadInto(z, Z, <, World.MaxZ, isSun, MIN, MAX)
|
||||
if (ln.brightness == 0) { continue; }
|
||||
|
||||
BlockID thisBlock = World_GetBlock(ln.coords.x, ln.coords.y, ln.coords.z);
|
||||
ln.brightness--;
|
||||
if (ln.brightness == 0) continue;
|
||||
|
||||
ln.coords.x--;
|
||||
Light_TrySpreadInto(x, X, > , 0, isSun, MAX, MIN)
|
||||
ln.coords.x += 2;
|
||||
Light_TrySpreadInto(x, X, < , World.MaxX, isSun, MIN, MAX)
|
||||
ln.coords.x--;
|
||||
|
||||
//ln.coords.y--;
|
||||
//Light_TrySpreadInto(y, Y, >, 0, isSun, MAX, MIN)
|
||||
//ln.coords.y += 2;
|
||||
//Light_TrySpreadInto(y, Y, <, World.MaxY, isSun, MIN, MAX)
|
||||
//ln.coords.y--;
|
||||
|
||||
ln.coords.z--;
|
||||
Light_TrySpreadInto(z, Z, > , 0, isSun, MAX, MIN)
|
||||
ln.coords.z += 2;
|
||||
Light_TrySpreadInto(z, Z, < , World.MaxZ, isSun, MIN, MAX)
|
||||
}
|
||||
}
|
||||
|
||||
#define BlockBlockBrightness(curBlock) (Blocks.Brightness[curBlock] & MODERN_LIGHTING_MAX_LEVEL)
|
||||
#define BlockBlockBrightness(curBlock) (curBlock == 201 ? 10 : (Blocks.Brightness[curBlock] & MODERN_LIGHTING_MAX_LEVEL))
|
||||
#define BlockSunBrightness(curBlock) (Blocks.Brightness[curBlock] >> MODERN_LIGHTING_SUN_SHIFT)
|
||||
static void CalculateChunkLightingSelf(int chunkIndex, int cx, int cy, int cz) {
|
||||
int x, y, z;
|
||||
@ -284,14 +293,18 @@ static void CalculateChunkLightingSelf(int chunkIndex, int cx, int cy, int cz) {
|
||||
for (x = chunkStartX; x < chunkEndX; x++) {
|
||||
|
||||
curBlock = World_GetBlock(x, y, z);
|
||||
|
||||
if (Blocks.Brightness[curBlock] > 0) {
|
||||
|
||||
brightness = BlockBlockBrightness(curBlock);
|
||||
|
||||
if (brightness > 0) { CalcLight(brightness, x, y, z, false, false); }
|
||||
if (brightness > 0) {
|
||||
struct LightNode entry = { x, y, z, brightness };
|
||||
Queue_Enqueue(&lightQueue, &entry);
|
||||
}
|
||||
else {
|
||||
/* If no block brightness, it must use sun brightness */
|
||||
CalcLight(BlockSunBrightness(curBlock), x, y, z, true, false);
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,6 +314,9 @@ static void CalculateChunkLightingSelf(int chunkIndex, int cx, int cy, int cz) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlushLightQueue(false, false);
|
||||
|
||||
chunkLightingDataFlags[chunkIndex] = CHUNK_SELF_CALCULATED;
|
||||
}
|
||||
|
||||
@ -357,8 +373,66 @@ static void CalculateChunkLightingAll(int chunkIndex, int cx, int cy, int cz, cc
|
||||
chunkLightingDataFlags[chunkIndex] = CHUNK_ALL_CALCULATED;
|
||||
}
|
||||
|
||||
static void CalcUnlight(int x, int y, int z, cc_uint8 lightData, cc_bool sun) {
|
||||
|
||||
#define Light_TryUnSpreadInto(axis, dir, limit, Type) \
|
||||
if (neighborCoords.axis dir ## = limit) { \
|
||||
neighborBrightness = GetBlocklight(neighborCoords.x, neighborCoords.y, neighborCoords.z, isSun); \
|
||||
neighborBlockBrightness = Block ## Type ## Brightness(World_GetBlock(neighborCoords.x, neighborCoords.y, neighborCoords.z)); \
|
||||
/* This spot is a light caster, mark this spot as needing to be re-spread */ \
|
||||
if (neighborBlockBrightness > 0) { \
|
||||
struct LightNode entry = { neighborCoords.x, neighborCoords.y, neighborCoords.z, neighborBlockBrightness }; \
|
||||
Queue_Enqueue(&lightQueue, &entry); \
|
||||
} \
|
||||
if (neighborBrightness > 0) { \
|
||||
/* This neighbor is darker than cur spot, darken it*/ \
|
||||
if (neighborBrightness < curNode.brightness) { \
|
||||
SetBlocklight(0, neighborCoords.x, neighborCoords.y, neighborCoords.z, isSun, true); \
|
||||
struct LightNode neighborNode = { { neighborCoords.x, neighborCoords.y, neighborCoords.z }, neighborBrightness }; \
|
||||
Queue_Enqueue(&unlightQueue, &neighborNode); \
|
||||
} \
|
||||
/* This spot is brighter or same, mark this spot as needing to be re-spread */ \
|
||||
else { \
|
||||
Platform_Log1("Spread for me. %i", &neighborBrightness); \
|
||||
struct LightNode entry = { neighborCoords.x, neighborCoords.y, neighborCoords.z, neighborBrightness }; \
|
||||
Queue_Enqueue(&lightQueue, &entry); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
|
||||
static void CalcUnlight(int x, int y, int z, cc_uint8 brightness, cc_bool isSun) {
|
||||
SetBlocklight(0, x, y, z, isSun, true);
|
||||
struct LightNode sourceNode = { { x, y, z }, brightness };
|
||||
Queue_Enqueue(&unlightQueue, &sourceNode);
|
||||
|
||||
while (unlightQueue.count > 0) {
|
||||
struct LightNode curNode = *(struct LightNode*)(Queue_Dequeue(&unlightQueue));
|
||||
cc_uint8 neighborBrightness;
|
||||
cc_uint8 neighborBlockBrightness;
|
||||
//Step 4: Look at all neighbouring voxels to that node.
|
||||
// if their light level is nonzero and is
|
||||
// less than the current lightLevel, then add them to the
|
||||
// queue and set their light level to zero.
|
||||
// Else if it is >= current node, then add it to
|
||||
// the light propagation queue.
|
||||
|
||||
IVec3 neighborCoords = curNode.coords;
|
||||
|
||||
neighborCoords.x--;
|
||||
Light_TryUnSpreadInto(x, >, 0, Block)
|
||||
neighborCoords.x += 2;
|
||||
Light_TryUnSpreadInto(x, <, World.MaxX, Block)
|
||||
neighborCoords.x--;
|
||||
|
||||
|
||||
|
||||
neighborCoords.z--;
|
||||
Light_TryUnSpreadInto(z, >, 0, Block)
|
||||
neighborCoords.z += 2;
|
||||
Light_TryUnSpreadInto(x, <, World.MaxZ, Block)
|
||||
neighborCoords.z--;
|
||||
}
|
||||
|
||||
FlushLightQueue(isSun, true);
|
||||
}
|
||||
static void ModernLighting_OnBlockChanged(int x, int y, int z, BlockID oldBlock, BlockID newBlock) {
|
||||
cc_uint8 lightData;
|
||||
@ -366,50 +440,45 @@ static void ModernLighting_OnBlockChanged(int x, int y, int z, BlockID oldBlock,
|
||||
int chunkCoordsIndex;
|
||||
BlockID thereBlock;
|
||||
cc_uint8 blockLightThere;
|
||||
cc_bool newBlockFullOpaque = IsFullOpaque(newBlock);
|
||||
|
||||
/* For some reason this is a possible case */
|
||||
if (oldBlock == newBlock) { return; }
|
||||
|
||||
ClassicLighting_OnBlockChanged(x, y, z, oldBlock, newBlock);
|
||||
|
||||
/* No lighting change */
|
||||
if (IsFullOpaque(oldBlock) && IsFullOpaque(newBlock)) { return; }
|
||||
|
||||
cc_uint8 oldBlockLightLevel = BlockBlockBrightness(oldBlock);
|
||||
cc_uint8 newBlockLightLevel = BlockBlockBrightness(newBlock);
|
||||
|
||||
cc_uint8 oldBlockLightLevelHere = GetBlocklight(x, y, z, false);
|
||||
cc_uint8 oldLightLevelHere = GetBlocklight(x, y, z, false);
|
||||
|
||||
/* The new block casts light (therefore doesn't block light) */
|
||||
if (newBlockLightLevel > 0) {
|
||||
|
||||
/* doesn't brighten this spot, no change */
|
||||
if (oldBlockLightLevelHere >= newBlockLightLevel) { return; }
|
||||
|
||||
Platform_LogConst("Adding light");
|
||||
/* brightens this spot, recalculate lighting */
|
||||
CalcLight(newBlockLightLevel, x, y, z, false, true);
|
||||
/* Cel has no lighting and new block doesn't cast light and blocks all light, no change */
|
||||
if (!oldLightLevelHere && !newBlockLightLevel && newBlockFullOpaque) {
|
||||
Platform_LogConst("No change");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Light passes through both and new block does not cast light, no change */
|
||||
if (IsFullTransparent(oldBlock) && IsFullTransparent(newBlock)) { return; }
|
||||
|
||||
//incomplete stuff
|
||||
//cc_bool oldLightPasses;
|
||||
//cc_bool newLightPasses;
|
||||
//
|
||||
//x--;
|
||||
//thereBlock = World_GetBlock(x, y, z);
|
||||
//oldLightPasses = CanLightPass(oldBlock, FACE_XMIN) && CanLightPass(thereBlock, FACE_XMAX);
|
||||
//newLightPasses = CanLightPass(newBlock, FACE_XMIN) && CanLightPass(thereBlock, FACE_XMAX);
|
||||
///* Here to x-1 has been sealed */
|
||||
//if (oldLightPasses && !newLightPasses) {
|
||||
//
|
||||
//}
|
||||
//blockLightThere = GetBlocklight(x, y, z, false);
|
||||
//
|
||||
//if (CanLightPass(newBlock, FACE_XMIN) && CanLightPass(World_GetBlock(x, y, z), FACE_XMAX)) {
|
||||
// //if ()
|
||||
//}
|
||||
/* Cel is darker than the new block, only brighter case */
|
||||
if (oldLightLevelHere < newBlockLightLevel) {
|
||||
/* brighten this spot, recalculate lighting */
|
||||
Platform_LogConst("Brightening");
|
||||
struct LightNode entry = { x, y, z, newBlockLightLevel };
|
||||
Queue_Enqueue(&lightQueue, &entry);
|
||||
FlushLightQueue(false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Light passes through old and new, old block does not cast light, new block does not cast light; no change */
|
||||
if (IsFullTransparent(oldBlock) && IsFullTransparent(newBlock) && !oldBlockLightLevel && !newBlockLightLevel) {
|
||||
Platform_LogConst("Light passes through old and new and new block does not cast light, no change");
|
||||
return;
|
||||
}
|
||||
|
||||
CalcUnlight(x, y, z, oldLightLevelHere, false);
|
||||
return;
|
||||
|
||||
|
||||
/* lazily recalculate all lighting in this chunkand neighbors, TODO optimise this */
|
||||
//CalculateChunkLightingAll(chunkIndex, cx, cy, cz, true);
|
||||
|
Loading…
x
Reference in New Issue
Block a user