diff options
author | cedeel@gmail.com <cedeel@gmail.com@0a769ca7-a7f5-676a-18bf-c427514a06d6> | 2012-06-14 15:06:06 +0200 |
---|---|---|
committer | cedeel@gmail.com <cedeel@gmail.com@0a769ca7-a7f5-676a-18bf-c427514a06d6> | 2012-06-14 15:06:06 +0200 |
commit | 92c59963f82f81aa3202657e7fdbb2592924ede3 (patch) | |
tree | b7eb2474528a4998fa102e3ec9119b908cee08b4 /source/LightingThread.cpp | |
parent | Added HOOK_WEATHER_CHANGE. (diff) | |
download | cuberite-92c59963f82f81aa3202657e7fdbb2592924ede3.tar cuberite-92c59963f82f81aa3202657e7fdbb2592924ede3.tar.gz cuberite-92c59963f82f81aa3202657e7fdbb2592924ede3.tar.bz2 cuberite-92c59963f82f81aa3202657e7fdbb2592924ede3.tar.lz cuberite-92c59963f82f81aa3202657e7fdbb2592924ede3.tar.xz cuberite-92c59963f82f81aa3202657e7fdbb2592924ede3.tar.zst cuberite-92c59963f82f81aa3202657e7fdbb2592924ede3.zip |
Diffstat (limited to '')
-rw-r--r-- | source/LightingThread.cpp | 1062 |
1 files changed, 531 insertions, 531 deletions
diff --git a/source/LightingThread.cpp b/source/LightingThread.cpp index c2f876e32..67f3b4116 100644 --- a/source/LightingThread.cpp +++ b/source/LightingThread.cpp @@ -1,531 +1,531 @@ -
-// LightingThread.cpp
-
-// Implements the cLightingThread class representing the thread that processes requests for lighting
-
-#include "Globals.h"
-#include "LightingThread.h"
-#include "cChunkMap.h"
-#include "cWorld.h"
-
-
-
-
-
-/// If more than this many chunks are in the queue, a warning is printed to the log
-#define WARN_ON_QUEUE_SIZE 800
-
-
-
-
-
-/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]:
-class cReader :
- public cChunkDataCallback
-{
- virtual void BlockTypes(const BLOCKTYPE * a_Type) override
- {
- // ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that)
- // C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :)
- typedef struct {BLOCKTYPE m_Row[16]; } ROW;
- ROW * InputRows = (ROW *)a_Type;
- ROW * OutputRows = (ROW *)m_BlockTypes;
- int InputIdx = 0;
- int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- OutputRows[OutputIdx] = InputRows[InputIdx++];
- OutputIdx += 3;
- } // for z
- // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows
- // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip
- OutputIdx += cChunkDef::Width * 6;
- } // for y
- } // BlockTypes()
-
-
- virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override
- {
- typedef struct {HEIGHTTYPE m_Row[16]; } ROW;
- ROW * InputRows = (ROW *)a_Heightmap;
- ROW * OutputRows = (ROW *)m_HeightMap;
- int InputIdx = 0;
- int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- OutputRows[OutputIdx] = InputRows[InputIdx++];
- OutputIdx += 3;
- } // for z
- }
-
-public:
- int m_ReadingChunkX; // 0, 1 or 2; x-offset of the chunk we're reading from the BlockTypes start
- int m_ReadingChunkZ; // 0, 1 or 2; z-offset of the chunk we're reading from the BlockTypes start
- BLOCKTYPE * m_BlockTypes; // 3x3 chunks of block types, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
- HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cLightingThread:
-
-cLightingThread::cLightingThread(void) :
- super("cLightingThread"),
- m_World(NULL)
-{
-}
-
-
-
-
-
-cLightingThread::~cLightingThread()
-{
- Stop();
-}
-
-
-
-
-
-bool cLightingThread::Start(cWorld * a_World)
-{
- ASSERT(m_World == NULL); // Not started yet
- m_World = a_World;
-
- return super::Start();
-}
-
-
-
-
-
-void cLightingThread::Stop(void)
-{
- {
- cCSLock Lock(m_CS);
- m_Queue.clear();
- }
- m_ShouldTerminate = true;
- m_evtItemAdded.Set();
-
- Wait();
-}
-
-
-
-
-
-void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter)
-{
- ASSERT(m_World != NULL); // Did you call Start() properly?
-
- cChunkStay * ChunkStay = new cChunkStay(m_World);
- ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ + 1);
- ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ);
- ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ - 1);
- ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ + 1);
- ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
- ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ - 1);
- ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ + 1);
- ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ);
- ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ - 1);
- ChunkStay->Enable();
- ChunkStay->Load();
- cCSLock Lock(m_CS);
- m_Queue.push_back(sItem(a_ChunkX, a_ChunkZ, ChunkStay, a_CallbackAfter));
- if (m_Queue.size() > WARN_ON_QUEUE_SIZE)
- {
- LOGINFO("Lighting thread overloaded, %d items in queue", m_Queue.size());
- }
- m_evtItemAdded.Set();
-}
-
-
-
-
-
-void cLightingThread::WaitForQueueEmpty(void)
-{
- cCSLock Lock(m_CS);
- while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PostponedQueue.empty()))
- {
- cCSUnlock Unlock(Lock);
- m_evtQueueEmpty.Wait();
- }
-}
-
-
-
-
-
-size_t cLightingThread::GetQueueLength(void)
-{
- cCSLock Lock(m_CS);
- return m_Queue.size() + m_PostponedQueue.size();
-}
-
-
-
-
-
-void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ)
-{
- // Check all the items in the m_PostponedQueue, if the chunk is their neighbor, move the item to m_Queue
-
- bool NewlyAdded = false;
- {
- cCSLock Lock(m_CS);
- for (sItems::iterator itr = m_PostponedQueue.begin(); itr != m_PostponedQueue.end(); )
- {
- if (
- (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) &&
- (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1)
- )
- {
- // It is a neighbor
- m_Queue.push_back(*itr);
- itr = m_PostponedQueue.erase(itr);
- NewlyAdded = true;
- }
- else
- {
- ++itr;
- }
- } // for itr - m_PostponedQueue[]
- } // Lock(m_CS)
-
- if (NewlyAdded)
- {
- m_evtItemAdded.Set(); // Notify the thread it has some work to do
- }
-}
-
-
-
-
-
-void cLightingThread::Execute(void)
-{
- while (true)
- {
- {
- cCSLock Lock(m_CS);
- if (m_Queue.size() == 0)
- {
- cCSUnlock Unlock(Lock);
- m_evtItemAdded.Wait();
- }
- }
-
- if (m_ShouldTerminate)
- {
- return;
- }
-
- // Process one items from the queue:
- sItem Item;
- {
- cCSLock Lock(m_CS);
- if (m_Queue.empty())
- {
- continue;
- }
- Item = m_Queue.front();
- m_Queue.pop_front();
- if (m_Queue.empty())
- {
- m_evtQueueEmpty.Set();
- }
- } // CSLock(m_CS)
-
- LightChunk(Item);
- }
-}
-
-
-
-
-
-
-void cLightingThread::LightChunk(cLightingThread::sItem & a_Item)
-{
- cChunkDef::BlockNibbles BlockLight, SkyLight;
-
- if (!ReadChunks(a_Item.x, a_Item.z))
- {
- // Neighbors not available. Re-queue in the postponed queue
- cCSLock Lock(m_CS);
- m_PostponedQueue.push_back(a_Item);
- return;
- }
-
- /*
- // DEBUG: torch somewhere:
- m_BlockTypes[19 + 24 * cChunkDef::Width * 3 + (m_HeightMap[24 + 24 * cChunkDef::Width * 3] / 2) * BlocksPerYLayer] = E_BLOCK_TORCH;
- // m_HeightMap[24 + 24 * cChunkDef::Width * 3]++;
- */
-
- PrepareBlockLight();
- CalcLight(m_BlockLight);
-
- PrepareSkyLight();
- CalcLight(m_SkyLight);
-
- CompressLight(m_BlockLight, BlockLight);
- CompressLight(m_SkyLight, SkyLight);
-
- /*
- // DEBUG:
- {
- cFile f("chunk_BlockTypes.dat", cFile::fmWrite);
- if (f.IsOpen())
- {
- f.Write(m_BlockTypes, sizeof(m_BlockTypes));
- }
- }
-
- // DEBUG:
- {
- cFile f("Chunk_SkyLight.dat", cFile::fmWrite);
- if (f.IsOpen())
- {
- f.Write(m_SkyLight, sizeof(m_SkyLight));
- }
- }
-
- // DEBUG:
- {
- cFile f("Chunk_BlockLight.dat", cFile::fmWrite);
- if (f.IsOpen())
- {
- f.Write(m_BlockLight, sizeof(m_BlockLight));
- }
- }
- */
-
- m_World->ChunkLighted(a_Item.x, a_Item.z, BlockLight, SkyLight);
-
- if (a_Item.m_Callback != NULL)
- {
- a_Item.m_Callback->Call(a_Item.x, a_Item.z);
- }
- delete a_Item.m_ChunkStay;
-}
-
-
-
-
-
-bool cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ)
-{
- cReader Reader;
- Reader.m_BlockTypes = m_BlockTypes;
- Reader.m_HeightMap = m_HeightMap;
-
- for (int z = 0; z < 3; z++)
- {
- Reader.m_ReadingChunkZ = z;
- for (int x = 0; x < 3; x++)
- {
- Reader.m_ReadingChunkX = x;
- if (!m_World->GetChunkData(a_ChunkX + x - 1, ZERO_CHUNK_Y, a_ChunkZ + z - 1, Reader))
- {
- return false;
- }
- } // for z
- } // for x
-
- memset(m_BlockLight, 0, sizeof(m_BlockLight));
- memset(m_SkyLight, 0, sizeof(m_SkyLight));
- return true;
-}
-
-
-
-
-
-void cLightingThread::PrepareSkyLight(void)
-{
- // Clear seeds:
- memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
- m_NumSeeds = 0;
-
- // Walk every column that has all XZ neighbors
- for (int z = 1; z < cChunkDef::Width * 3 - 1; z++)
- {
- int BaseZ = z * cChunkDef::Width * 3;
- for (int x = 1; x < cChunkDef::Width * 3 - 1; x++)
- {
- int idx = BaseZ + x;
- int Current = m_HeightMap[idx] + 1;
- int Neighbor1 = m_HeightMap[idx + 1] + 1; // X + 1
- int Neighbor2 = m_HeightMap[idx - 1] + 1; // X - 1
- int Neighbor3 = m_HeightMap[idx + cChunkDef::Width * 3] + 1; // Z + 1
- int Neighbor4 = m_HeightMap[idx - cChunkDef::Width * 3] + 1; // Z - 1
- int MaxNeighbor = MAX(MAX(Neighbor1, Neighbor2), MAX(Neighbor3, Neighbor4)); // Maximum of the four neighbors
-
- // TODO: The following cycle can be transofrmed into two separate cycles with no condition inside them, one lighting and the other seeding
- for (int y = Current, Index = idx + y * BlocksPerYLayer; y < cChunkDef::Height; y++, Index += BlocksPerYLayer)
- {
- // If all the XZ neighbors are lower than y, abort for the current column (but light up the rest of it):
- if (y >= MaxNeighbor)
- {
- for (int y2 = y; y2 < cChunkDef::Height; y2++, Index += BlocksPerYLayer)
- {
- m_SkyLight[Index] = 15;
- } // for y2
- break; // for y
- }
-
- // Add current block as a seed:
- m_IsSeed1[Index] = true;
- m_SeedIdx1[m_NumSeeds++] = Index;
-
- // Light it up to full skylight:
- m_SkyLight[Index] = 15;
- }
- }
- }
-}
-
-
-
-
-
-void cLightingThread::PrepareBlockLight(void)
-{
- // Clear seeds:
- memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
- m_NumSeeds = 0;
-
- // Walk every column that has all XZ neighbors, make a seed for each light-emitting block:
- for (int z = 1; z < cChunkDef::Width * 3 - 1; z++)
- {
- int BaseZ = z * cChunkDef::Width * 3;
- for (int x = 1; x < cChunkDef::Width * 3 - 1; x++)
- {
- int idx = BaseZ + x;
- for (int y = m_HeightMap[idx], Index = idx + y * BlocksPerYLayer; y >= 0; y--, Index -= BlocksPerYLayer)
- {
- if (g_BlockLightValue[m_BlockTypes[Index]] == 0)
- {
- continue;
- }
-
- // Add current block as a seed:
- m_IsSeed1[Index] = true;
- m_SeedIdx1[m_NumSeeds++] = Index;
-
- // Light it up:
- m_BlockLight[Index] = g_BlockLightValue[m_BlockTypes[Index]];
- }
- }
- }
-}
-
-
-
-
-
-void cLightingThread::CalcLight(NIBBLETYPE * a_Light)
-{
- int NumSeeds2 = 0;
- while (m_NumSeeds > 0)
- {
- // Buffer 1 -> buffer 2
- memset(m_IsSeed2, 0, sizeof(m_IsSeed2));
- NumSeeds2 = 0;
- CalcLightStep(a_Light, m_NumSeeds, m_IsSeed1, m_SeedIdx1, NumSeeds2, m_IsSeed2, m_SeedIdx2);
- if (NumSeeds2 == 0)
- {
- return;
- }
-
- // Buffer 2 -> buffer 1
- memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
- m_NumSeeds = 0;
- CalcLightStep(a_Light, NumSeeds2, m_IsSeed2, m_SeedIdx2, m_NumSeeds, m_IsSeed1, m_SeedIdx1);
- }
-}
-
-
-
-
-
-void cLightingThread::CalcLightStep(
- NIBBLETYPE * a_Light,
- int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn,
- int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
-)
-{
- int NumSeedsOut = 0;
- for (int i = 0; i < a_NumSeedsIn; i++)
- {
- int SeedIdx = a_SeedIdxIn[i];
- int SeedX = SeedIdx % (cChunkDef::Width * 3);
- int SeedZ = (SeedIdx / (cChunkDef::Width * 3)) % (cChunkDef::Width * 3);
- int SeedY = SeedIdx / BlocksPerYLayer;
-
- // Propagate seed:
- if (SeedX < cChunkDef::Width * 3)
- {
- PropagateLight(a_Light, SeedIdx, SeedIdx + 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
- }
- if (SeedX > 0)
- {
- PropagateLight(a_Light, SeedIdx, SeedIdx - 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
- }
- if (SeedZ < cChunkDef::Width * 3)
- {
- PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
- }
- if (SeedZ > 0)
- {
- PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
- }
- if (SeedY < cChunkDef::Height)
- {
- PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
- }
- if (SeedY > 0)
- {
- PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
- }
- } // for i - a_SeedIdxIn[]
- a_NumSeedsOut = NumSeedsOut;
-}
-
-
-
-
-
-void cLightingThread::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight)
-{
- int InIdx = cChunkDef::Width * 49; // Index to the first nibble of the middle chunk in the a_LightArray
- int OutIdx = 0;
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x += 2)
- {
- a_ChunkLight[OutIdx++] = (a_LightArray[InIdx + 1] << 4) | a_LightArray[InIdx];
- InIdx += 2;
- }
- InIdx += cChunkDef::Width * 2;
- }
- // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows
- // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip
- InIdx += cChunkDef::Width * cChunkDef::Width * 6;
- }
-}
-
-
-
-
+ +// LightingThread.cpp + +// Implements the cLightingThread class representing the thread that processes requests for lighting + +#include "Globals.h" +#include "LightingThread.h" +#include "cChunkMap.h" +#include "cWorld.h" + + + + + +/// If more than this many chunks are in the queue, a warning is printed to the log +#define WARN_ON_QUEUE_SIZE 800 + + + + + +/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]: +class cReader : + public cChunkDataCallback +{ + virtual void BlockTypes(const BLOCKTYPE * a_Type) override + { + // ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that) + // C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :) + typedef struct {BLOCKTYPE m_Row[16]; } ROW; + ROW * InputRows = (ROW *)a_Type; + ROW * OutputRows = (ROW *)m_BlockTypes; + int InputIdx = 0; + int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + OutputRows[OutputIdx] = InputRows[InputIdx++]; + OutputIdx += 3; + } // for z + // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows + // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip + OutputIdx += cChunkDef::Width * 6; + } // for y + } // BlockTypes() + + + virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override + { + typedef struct {HEIGHTTYPE m_Row[16]; } ROW; + ROW * InputRows = (ROW *)a_Heightmap; + ROW * OutputRows = (ROW *)m_HeightMap; + int InputIdx = 0; + int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; + for (int z = 0; z < cChunkDef::Width; z++) + { + OutputRows[OutputIdx] = InputRows[InputIdx++]; + OutputIdx += 3; + } // for z + } + +public: + int m_ReadingChunkX; // 0, 1 or 2; x-offset of the chunk we're reading from the BlockTypes start + int m_ReadingChunkZ; // 0, 1 or 2; z-offset of the chunk we're reading from the BlockTypes start + BLOCKTYPE * m_BlockTypes; // 3x3 chunks of block types, organized as a single XZY blob of data (instead of 3x3 XZY blobs) + HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs) +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLightingThread: + +cLightingThread::cLightingThread(void) : + super("cLightingThread"), + m_World(NULL) +{ +} + + + + + +cLightingThread::~cLightingThread() +{ + Stop(); +} + + + + + +bool cLightingThread::Start(cWorld * a_World) +{ + ASSERT(m_World == NULL); // Not started yet + m_World = a_World; + + return super::Start(); +} + + + + + +void cLightingThread::Stop(void) +{ + { + cCSLock Lock(m_CS); + m_Queue.clear(); + } + m_ShouldTerminate = true; + m_evtItemAdded.Set(); + + Wait(); +} + + + + + +void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter) +{ + ASSERT(m_World != NULL); // Did you call Start() properly? + + cChunkStay * ChunkStay = new cChunkStay(m_World); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Enable(); + ChunkStay->Load(); + cCSLock Lock(m_CS); + m_Queue.push_back(sItem(a_ChunkX, a_ChunkZ, ChunkStay, a_CallbackAfter)); + if (m_Queue.size() > WARN_ON_QUEUE_SIZE) + { + LOGINFO("Lighting thread overloaded, %d items in queue", m_Queue.size()); + } + m_evtItemAdded.Set(); +} + + + + + +void cLightingThread::WaitForQueueEmpty(void) +{ + cCSLock Lock(m_CS); + while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PostponedQueue.empty())) + { + cCSUnlock Unlock(Lock); + m_evtQueueEmpty.Wait(); + } +} + + + + + +size_t cLightingThread::GetQueueLength(void) +{ + cCSLock Lock(m_CS); + return m_Queue.size() + m_PostponedQueue.size(); +} + + + + + +void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ) +{ + // Check all the items in the m_PostponedQueue, if the chunk is their neighbor, move the item to m_Queue + + bool NewlyAdded = false; + { + cCSLock Lock(m_CS); + for (sItems::iterator itr = m_PostponedQueue.begin(); itr != m_PostponedQueue.end(); ) + { + if ( + (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) && + (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) + ) + { + // It is a neighbor + m_Queue.push_back(*itr); + itr = m_PostponedQueue.erase(itr); + NewlyAdded = true; + } + else + { + ++itr; + } + } // for itr - m_PostponedQueue[] + } // Lock(m_CS) + + if (NewlyAdded) + { + m_evtItemAdded.Set(); // Notify the thread it has some work to do + } +} + + + + + +void cLightingThread::Execute(void) +{ + while (true) + { + { + cCSLock Lock(m_CS); + if (m_Queue.size() == 0) + { + cCSUnlock Unlock(Lock); + m_evtItemAdded.Wait(); + } + } + + if (m_ShouldTerminate) + { + return; + } + + // Process one items from the queue: + sItem Item; + { + cCSLock Lock(m_CS); + if (m_Queue.empty()) + { + continue; + } + Item = m_Queue.front(); + m_Queue.pop_front(); + if (m_Queue.empty()) + { + m_evtQueueEmpty.Set(); + } + } // CSLock(m_CS) + + LightChunk(Item); + } +} + + + + + + +void cLightingThread::LightChunk(cLightingThread::sItem & a_Item) +{ + cChunkDef::BlockNibbles BlockLight, SkyLight; + + if (!ReadChunks(a_Item.x, a_Item.z)) + { + // Neighbors not available. Re-queue in the postponed queue + cCSLock Lock(m_CS); + m_PostponedQueue.push_back(a_Item); + return; + } + + /* + // DEBUG: torch somewhere: + m_BlockTypes[19 + 24 * cChunkDef::Width * 3 + (m_HeightMap[24 + 24 * cChunkDef::Width * 3] / 2) * BlocksPerYLayer] = E_BLOCK_TORCH; + // m_HeightMap[24 + 24 * cChunkDef::Width * 3]++; + */ + + PrepareBlockLight(); + CalcLight(m_BlockLight); + + PrepareSkyLight(); + CalcLight(m_SkyLight); + + CompressLight(m_BlockLight, BlockLight); + CompressLight(m_SkyLight, SkyLight); + + /* + // DEBUG: + { + cFile f("chunk_BlockTypes.dat", cFile::fmWrite); + if (f.IsOpen()) + { + f.Write(m_BlockTypes, sizeof(m_BlockTypes)); + } + } + + // DEBUG: + { + cFile f("Chunk_SkyLight.dat", cFile::fmWrite); + if (f.IsOpen()) + { + f.Write(m_SkyLight, sizeof(m_SkyLight)); + } + } + + // DEBUG: + { + cFile f("Chunk_BlockLight.dat", cFile::fmWrite); + if (f.IsOpen()) + { + f.Write(m_BlockLight, sizeof(m_BlockLight)); + } + } + */ + + m_World->ChunkLighted(a_Item.x, a_Item.z, BlockLight, SkyLight); + + if (a_Item.m_Callback != NULL) + { + a_Item.m_Callback->Call(a_Item.x, a_Item.z); + } + delete a_Item.m_ChunkStay; +} + + + + + +bool cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ) +{ + cReader Reader; + Reader.m_BlockTypes = m_BlockTypes; + Reader.m_HeightMap = m_HeightMap; + + for (int z = 0; z < 3; z++) + { + Reader.m_ReadingChunkZ = z; + for (int x = 0; x < 3; x++) + { + Reader.m_ReadingChunkX = x; + if (!m_World->GetChunkData(a_ChunkX + x - 1, ZERO_CHUNK_Y, a_ChunkZ + z - 1, Reader)) + { + return false; + } + } // for z + } // for x + + memset(m_BlockLight, 0, sizeof(m_BlockLight)); + memset(m_SkyLight, 0, sizeof(m_SkyLight)); + return true; +} + + + + + +void cLightingThread::PrepareSkyLight(void) +{ + // Clear seeds: + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + m_NumSeeds = 0; + + // Walk every column that has all XZ neighbors + for (int z = 1; z < cChunkDef::Width * 3 - 1; z++) + { + int BaseZ = z * cChunkDef::Width * 3; + for (int x = 1; x < cChunkDef::Width * 3 - 1; x++) + { + int idx = BaseZ + x; + int Current = m_HeightMap[idx] + 1; + int Neighbor1 = m_HeightMap[idx + 1] + 1; // X + 1 + int Neighbor2 = m_HeightMap[idx - 1] + 1; // X - 1 + int Neighbor3 = m_HeightMap[idx + cChunkDef::Width * 3] + 1; // Z + 1 + int Neighbor4 = m_HeightMap[idx - cChunkDef::Width * 3] + 1; // Z - 1 + int MaxNeighbor = MAX(MAX(Neighbor1, Neighbor2), MAX(Neighbor3, Neighbor4)); // Maximum of the four neighbors + + // TODO: The following cycle can be transofrmed into two separate cycles with no condition inside them, one lighting and the other seeding + for (int y = Current, Index = idx + y * BlocksPerYLayer; y < cChunkDef::Height; y++, Index += BlocksPerYLayer) + { + // If all the XZ neighbors are lower than y, abort for the current column (but light up the rest of it): + if (y >= MaxNeighbor) + { + for (int y2 = y; y2 < cChunkDef::Height; y2++, Index += BlocksPerYLayer) + { + m_SkyLight[Index] = 15; + } // for y2 + break; // for y + } + + // Add current block as a seed: + m_IsSeed1[Index] = true; + m_SeedIdx1[m_NumSeeds++] = Index; + + // Light it up to full skylight: + m_SkyLight[Index] = 15; + } + } + } +} + + + + + +void cLightingThread::PrepareBlockLight(void) +{ + // Clear seeds: + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + m_NumSeeds = 0; + + // Walk every column that has all XZ neighbors, make a seed for each light-emitting block: + for (int z = 1; z < cChunkDef::Width * 3 - 1; z++) + { + int BaseZ = z * cChunkDef::Width * 3; + for (int x = 1; x < cChunkDef::Width * 3 - 1; x++) + { + int idx = BaseZ + x; + for (int y = m_HeightMap[idx], Index = idx + y * BlocksPerYLayer; y >= 0; y--, Index -= BlocksPerYLayer) + { + if (g_BlockLightValue[m_BlockTypes[Index]] == 0) + { + continue; + } + + // Add current block as a seed: + m_IsSeed1[Index] = true; + m_SeedIdx1[m_NumSeeds++] = Index; + + // Light it up: + m_BlockLight[Index] = g_BlockLightValue[m_BlockTypes[Index]]; + } + } + } +} + + + + + +void cLightingThread::CalcLight(NIBBLETYPE * a_Light) +{ + int NumSeeds2 = 0; + while (m_NumSeeds > 0) + { + // Buffer 1 -> buffer 2 + memset(m_IsSeed2, 0, sizeof(m_IsSeed2)); + NumSeeds2 = 0; + CalcLightStep(a_Light, m_NumSeeds, m_IsSeed1, m_SeedIdx1, NumSeeds2, m_IsSeed2, m_SeedIdx2); + if (NumSeeds2 == 0) + { + return; + } + + // Buffer 2 -> buffer 1 + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + m_NumSeeds = 0; + CalcLightStep(a_Light, NumSeeds2, m_IsSeed2, m_SeedIdx2, m_NumSeeds, m_IsSeed1, m_SeedIdx1); + } +} + + + + + +void cLightingThread::CalcLightStep( + NIBBLETYPE * a_Light, + int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn, + int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut +) +{ + int NumSeedsOut = 0; + for (int i = 0; i < a_NumSeedsIn; i++) + { + int SeedIdx = a_SeedIdxIn[i]; + int SeedX = SeedIdx % (cChunkDef::Width * 3); + int SeedZ = (SeedIdx / (cChunkDef::Width * 3)) % (cChunkDef::Width * 3); + int SeedY = SeedIdx / BlocksPerYLayer; + + // Propagate seed: + if (SeedX < cChunkDef::Width * 3) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedX > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedZ < cChunkDef::Width * 3) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedZ > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedY < cChunkDef::Height) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedY > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + } // for i - a_SeedIdxIn[] + a_NumSeedsOut = NumSeedsOut; +} + + + + + +void cLightingThread::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight) +{ + int InIdx = cChunkDef::Width * 49; // Index to the first nibble of the middle chunk in the a_LightArray + int OutIdx = 0; + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x += 2) + { + a_ChunkLight[OutIdx++] = (a_LightArray[InIdx + 1] << 4) | a_LightArray[InIdx]; + InIdx += 2; + } + InIdx += cChunkDef::Width * 2; + } + // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows + // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip + InIdx += cChunkDef::Width * cChunkDef::Width * 6; + } +} + + + + |