2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
// LightingThread.cpp
|
|
|
|
|
|
|
|
// Implements the cLightingThread class representing the thread that processes requests for lighting
|
|
|
|
|
|
|
|
#include "Globals.h"
|
|
|
|
#include "LightingThread.h"
|
2012-09-23 18:09:57 -04:00
|
|
|
#include "ChunkMap.h"
|
2014-02-08 15:55:21 -05:00
|
|
|
#include "ChunkStay.h"
|
2012-09-23 18:09:57 -04:00
|
|
|
#include "World.h"
|
2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]:
|
|
|
|
class cReader :
|
|
|
|
public cChunkDataCallback
|
|
|
|
{
|
2014-05-21 14:58:48 -04:00
|
|
|
virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2014-04-26 13:50:23 -04:00
|
|
|
BLOCKTYPE * OutputRows = m_BlockTypes;
|
2012-06-14 09:06:06 -04:00
|
|
|
int InputIdx = 0;
|
|
|
|
int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
|
2014-06-16 16:53:08 -04:00
|
|
|
int MaxHeight = std::min(+cChunkDef::Height, m_MaxHeight + 16); // Need 16 blocks above the highest
|
2014-04-11 18:04:50 -04:00
|
|
|
for (int y = 0; y < MaxHeight; y++)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
for (int z = 0; z < cChunkDef::Width; z++)
|
|
|
|
{
|
2014-05-29 12:25:08 -04:00
|
|
|
a_ChunkBuffer.CopyBlockTypes(OutputRows + OutputIdx * 16, InputIdx * 16, 16);
|
2014-04-26 13:50:23 -04:00
|
|
|
InputIdx++;
|
2012-06-14 09:06:06 -04:00
|
|
|
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
|
|
|
|
{
|
2014-04-11 18:04:50 -04:00
|
|
|
// Copy the entire heightmap, distribute it into the 3x3 chunk blob:
|
2012-06-14 09:06:06 -04:00
|
|
|
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
|
2014-04-11 18:04:50 -04:00
|
|
|
|
|
|
|
// Find the highest block in the entire chunk, use it as a base for m_MaxHeight:
|
|
|
|
HEIGHTTYPE MaxHeight = m_MaxHeight;
|
|
|
|
for (size_t i = 0; i < ARRAYCOUNT(*a_Heightmap); i++)
|
|
|
|
{
|
|
|
|
if ((*a_Heightmap)[i] > MaxHeight)
|
|
|
|
{
|
|
|
|
MaxHeight = (*a_Heightmap)[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_MaxHeight = MaxHeight;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2014-04-11 18:04:50 -04:00
|
|
|
HEIGHTTYPE m_MaxHeight; // Maximum value in this chunk's heightmap
|
2012-06-14 09:06:06 -04:00
|
|
|
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)
|
2014-04-11 18:04:50 -04:00
|
|
|
|
|
|
|
cReader(BLOCKTYPE * a_BlockTypes, HEIGHTTYPE * a_HeightMap) :
|
2014-04-11 18:24:35 -04:00
|
|
|
m_MaxHeight(0),
|
2014-04-11 18:04:50 -04:00
|
|
|
m_BlockTypes(a_BlockTypes),
|
2014-04-11 18:24:35 -04:00
|
|
|
m_HeightMap(a_HeightMap)
|
2014-04-11 18:04:50 -04:00
|
|
|
{
|
|
|
|
}
|
2012-06-14 09:06:06 -04:00
|
|
|
} ;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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);
|
2014-02-08 15:55:21 -05:00
|
|
|
for (cChunkStays::iterator itr = m_PendingQueue.begin(), end = m_PendingQueue.end(); itr != end; ++itr)
|
2013-01-25 05:12:29 -05:00
|
|
|
{
|
2014-05-09 03:07:54 -04:00
|
|
|
(*itr)->Disable();
|
2014-02-08 15:55:21 -05:00
|
|
|
delete *itr;
|
|
|
|
}
|
|
|
|
m_PendingQueue.clear();
|
|
|
|
for (cChunkStays::iterator itr = m_Queue.begin(), end = m_Queue.end(); itr != end; ++itr)
|
|
|
|
{
|
2014-05-09 03:07:54 -04:00
|
|
|
(*itr)->Disable();
|
2014-02-08 15:55:21 -05:00
|
|
|
delete *itr;
|
2013-01-25 05:12:29 -05:00
|
|
|
}
|
2012-06-14 09:06:06 -04:00
|
|
|
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?
|
|
|
|
|
2014-02-08 15:55:21 -05:00
|
|
|
cChunkStay * ChunkStay = new cLightingChunkStay(*this, a_ChunkX, a_ChunkZ, a_CallbackAfter);
|
2014-02-08 16:33:42 -05:00
|
|
|
{
|
2014-02-08 16:35:45 -05:00
|
|
|
// The ChunkStay will enqueue itself using the QueueChunkStay() once it is fully loaded
|
|
|
|
// In the meantime, put it into the PendingQueue so that it can be removed when stopping the thread
|
2014-02-08 16:33:42 -05:00
|
|
|
cCSLock Lock(m_CS);
|
|
|
|
m_PendingQueue.push_back(ChunkStay);
|
|
|
|
}
|
2014-02-08 15:55:21 -05:00
|
|
|
ChunkStay->Enable(*m_World->GetChunkMap());
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLightingThread::WaitForQueueEmpty(void)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CS);
|
2014-02-08 15:55:21 -05:00
|
|
|
while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PendingQueue.empty()))
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
cCSUnlock Unlock(Lock);
|
|
|
|
m_evtQueueEmpty.Wait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t cLightingThread::GetQueueLength(void)
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CS);
|
2014-02-08 15:55:21 -05:00
|
|
|
return m_Queue.size() + m_PendingQueue.size();
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLightingThread::Execute(void)
|
|
|
|
{
|
2014-01-07 10:00:19 -05:00
|
|
|
for (;;)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
{
|
|
|
|
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:
|
2014-02-08 15:55:21 -05:00
|
|
|
cLightingChunkStay * Item;
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
cCSLock Lock(m_CS);
|
|
|
|
if (m_Queue.empty())
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2014-02-08 15:55:21 -05:00
|
|
|
Item = (cLightingChunkStay *)m_Queue.front();
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Queue.pop_front();
|
|
|
|
if (m_Queue.empty())
|
|
|
|
{
|
|
|
|
m_evtQueueEmpty.Set();
|
|
|
|
}
|
|
|
|
} // CSLock(m_CS)
|
|
|
|
|
2014-02-08 15:55:21 -05:00
|
|
|
LightChunk(*Item);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-02-08 15:55:21 -05:00
|
|
|
void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
cChunkDef::BlockNibbles BlockLight, SkyLight;
|
|
|
|
|
2014-02-08 15:55:21 -05:00
|
|
|
ReadChunks(a_Item.m_ChunkX, a_Item.m_ChunkZ);
|
2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
PrepareBlockLight();
|
|
|
|
CalcLight(m_BlockLight);
|
|
|
|
|
|
|
|
PrepareSkyLight();
|
|
|
|
|
|
|
|
/*
|
2013-05-05 10:48:18 -04:00
|
|
|
// DEBUG: Save chunk data with highlighted seeds for visual inspection:
|
|
|
|
cFile f4;
|
|
|
|
if (
|
2014-04-11 18:04:50 -04:00
|
|
|
f4.Open(Printf("Chunk_%d_%d_seeds.grab", a_Item.m_ChunkX, a_Item.m_ChunkZ), cFile::fmWrite)
|
2013-05-05 10:48:18 -04:00
|
|
|
)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2013-05-05 10:48:18 -04:00
|
|
|
for (int z = 0; z < cChunkDef::Width * 3; z++)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2013-05-05 10:48:18 -04:00
|
|
|
for (int y = cChunkDef::Height / 2; y >= 0; y--)
|
|
|
|
{
|
|
|
|
unsigned char Seeds [cChunkDef::Width * 3];
|
|
|
|
memcpy(Seeds, m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3);
|
|
|
|
for (int x = 0; x < cChunkDef::Width * 3; x++)
|
|
|
|
{
|
|
|
|
if (m_IsSeed1[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x])
|
|
|
|
{
|
|
|
|
Seeds[x] = E_BLOCK_DIAMOND_BLOCK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f4.Write(Seeds, cChunkDef::Width * 3);
|
|
|
|
}
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2014-04-11 18:04:50 -04:00
|
|
|
f4.Close();
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2013-05-05 10:48:18 -04:00
|
|
|
//*/
|
2012-06-14 09:06:06 -04:00
|
|
|
|
2013-05-05 10:48:18 -04:00
|
|
|
CalcLight(m_SkyLight);
|
2012-06-14 09:06:06 -04:00
|
|
|
|
2013-05-05 10:48:18 -04:00
|
|
|
/*
|
|
|
|
// DEBUG: Save XY slices of the chunk data and lighting for visual inspection:
|
|
|
|
cFile f1, f2, f3;
|
|
|
|
if (
|
2014-04-11 18:04:50 -04:00
|
|
|
f1.Open(Printf("Chunk_%d_%d_data.grab", a_Item.m_ChunkX, a_Item.m_ChunkZ), cFile::fmWrite) &&
|
|
|
|
f2.Open(Printf("Chunk_%d_%d_sky.grab", a_Item.m_ChunkX, a_Item.m_ChunkZ), cFile::fmWrite) &&
|
|
|
|
f3.Open(Printf("Chunk_%d_%d_glow.grab", a_Item.m_ChunkX, a_Item.m_ChunkZ), cFile::fmWrite)
|
2013-05-05 10:48:18 -04:00
|
|
|
)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2013-05-05 10:48:18 -04:00
|
|
|
for (int z = 0; z < cChunkDef::Width * 3; z++)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2013-05-05 10:48:18 -04:00
|
|
|
for (int y = cChunkDef::Height / 2; y >= 0; y--)
|
|
|
|
{
|
|
|
|
f1.Write(m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3);
|
|
|
|
unsigned char SkyLight [cChunkDef::Width * 3];
|
|
|
|
unsigned char BlockLight[cChunkDef::Width * 3];
|
|
|
|
for (int x = 0; x < cChunkDef::Width * 3; x++)
|
|
|
|
{
|
|
|
|
SkyLight[x] = m_SkyLight [y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4;
|
|
|
|
BlockLight[x] = m_BlockLight[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4;
|
|
|
|
}
|
|
|
|
f2.Write(SkyLight, cChunkDef::Width * 3);
|
|
|
|
f3.Write(BlockLight, cChunkDef::Width * 3);
|
|
|
|
}
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2014-04-11 18:04:50 -04:00
|
|
|
f1.Close();
|
|
|
|
f2.Close();
|
|
|
|
f3.Close();
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2013-05-05 10:48:18 -04:00
|
|
|
//*/
|
|
|
|
|
|
|
|
CompressLight(m_BlockLight, BlockLight);
|
|
|
|
CompressLight(m_SkyLight, SkyLight);
|
2012-06-14 09:06:06 -04:00
|
|
|
|
2014-02-08 15:55:21 -05:00
|
|
|
m_World->ChunkLighted(a_Item.m_ChunkX, a_Item.m_ChunkZ, BlockLight, SkyLight);
|
2012-06-14 09:06:06 -04:00
|
|
|
|
2014-02-08 15:55:21 -05:00
|
|
|
if (a_Item.m_CallbackAfter != NULL)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2014-02-08 15:55:21 -05:00
|
|
|
a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2014-04-12 16:35:04 -04:00
|
|
|
a_Item.Disable();
|
2014-02-08 15:55:21 -05:00
|
|
|
delete &a_Item;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-04-11 18:04:50 -04:00
|
|
|
void cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2014-04-11 18:04:50 -04:00
|
|
|
cReader Reader(m_BlockTypes, m_HeightMap);
|
2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
for (int z = 0; z < 3; z++)
|
|
|
|
{
|
|
|
|
Reader.m_ReadingChunkZ = z;
|
|
|
|
for (int x = 0; x < 3; x++)
|
|
|
|
{
|
|
|
|
Reader.m_ReadingChunkX = x;
|
2014-04-11 18:04:50 -04:00
|
|
|
VERIFY(m_World->GetChunkData(a_ChunkX + x - 1, a_ChunkZ + z - 1, Reader));
|
2012-06-14 09:06:06 -04:00
|
|
|
} // for z
|
|
|
|
} // for x
|
|
|
|
|
|
|
|
memset(m_BlockLight, 0, sizeof(m_BlockLight));
|
|
|
|
memset(m_SkyLight, 0, sizeof(m_SkyLight));
|
2014-04-11 18:04:50 -04:00
|
|
|
m_MaxHeight = Reader.m_MaxHeight;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2013-05-07 15:59:17 -04:00
|
|
|
int MaxNeighbor = std::max(std::max(Neighbor1, Neighbor2), std::max(Neighbor3, Neighbor4)); // Maximum of the four neighbors
|
2012-06-14 09:06:06 -04:00
|
|
|
|
2013-05-05 10:48:18 -04:00
|
|
|
// Fill the column from the top down to Current with all-light:
|
|
|
|
for (int y = cChunkDef::Height - 1, Index = idx + y * BlocksPerYLayer; y >= Current; y--, Index -= BlocksPerYLayer)
|
|
|
|
{
|
|
|
|
m_SkyLight[Index] = 15;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add Current as a seed:
|
2013-05-09 15:06:16 -04:00
|
|
|
if (Current < cChunkDef::Height)
|
|
|
|
{
|
|
|
|
int CurrentIdx = idx + Current * BlocksPerYLayer;
|
|
|
|
m_IsSeed1[CurrentIdx] = true;
|
|
|
|
m_SeedIdx1[m_NumSeeds++] = CurrentIdx;
|
|
|
|
}
|
2013-05-05 10:48:18 -04:00
|
|
|
|
|
|
|
// Add seed from Current up to the highest neighbor:
|
|
|
|
for (int y = Current + 1, Index = idx + y * BlocksPerYLayer; y < MaxNeighbor; y++, Index += BlocksPerYLayer)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
m_IsSeed1[Index] = true;
|
|
|
|
m_SeedIdx1[m_NumSeeds++] = Index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLightingThread::PrepareBlockLight(void)
|
|
|
|
{
|
|
|
|
// Clear seeds:
|
|
|
|
memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
|
2012-11-13 10:50:49 -05:00
|
|
|
memset(m_IsSeed2, 0, sizeof(m_IsSeed2));
|
2012-06-14 09:06:06 -04:00
|
|
|
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)
|
|
|
|
{
|
2014-03-01 14:34:19 -05:00
|
|
|
if (cBlockInfo::GetLightValue(m_BlockTypes[Index]) == 0)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add current block as a seed:
|
|
|
|
m_IsSeed1[Index] = true;
|
|
|
|
m_SeedIdx1[m_NumSeeds++] = Index;
|
|
|
|
|
|
|
|
// Light it up:
|
2014-03-01 14:34:19 -05:00
|
|
|
m_BlockLight[Index] = cBlockInfo::GetLightValue(m_BlockTypes[Index]);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-04-11 18:04:50 -04:00
|
|
|
void cLightingThread::PrepareBlockLight2(void)
|
|
|
|
{
|
|
|
|
// Clear seeds:
|
|
|
|
memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
|
|
|
|
memset(m_IsSeed2, 0, sizeof(m_IsSeed2));
|
|
|
|
m_NumSeeds = 0;
|
|
|
|
|
|
|
|
// Add each emissive block into the seeds:
|
|
|
|
for (int y = 0; y < m_MaxHeight; y++)
|
|
|
|
{
|
|
|
|
int BaseY = y * BlocksPerYLayer; // Partial offset into m_BlockTypes for the Y coord
|
|
|
|
for (int z = 1; z < cChunkDef::Width * 3 - 1; z++)
|
|
|
|
{
|
|
|
|
int HBaseZ = z * cChunkDef::Width * 3; // Partial offset into m_Heightmap for the Z coord
|
|
|
|
int BaseZ = BaseY + HBaseZ; // Partial offset into m_BlockTypes for the Y and Z coords
|
|
|
|
for (int x = 1; x < cChunkDef::Width * 3 - 1; x++)
|
|
|
|
{
|
|
|
|
int idx = BaseZ + x;
|
|
|
|
if (y > m_HeightMap[HBaseZ + x])
|
|
|
|
{
|
|
|
|
// We're above the heightmap, ignore the block
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (cBlockInfo::GetLightValue(m_BlockTypes[idx]) == 0)
|
|
|
|
{
|
|
|
|
// Not a light-emissive block
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add current block as a seed:
|
|
|
|
m_IsSeed1[idx] = true;
|
|
|
|
m_SeedIdx1[m_NumSeeds++] = idx;
|
|
|
|
|
|
|
|
// Light it up:
|
|
|
|
m_BlockLight[idx] = cBlockInfo::GetLightValue(m_BlockTypes[idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
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
|
|
|
|
)
|
|
|
|
{
|
2013-12-22 10:04:29 -05:00
|
|
|
UNUSED(a_IsSeedIn);
|
2012-06-14 09:06:06 -04:00
|
|
|
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:
|
2012-11-16 04:40:15 -05:00
|
|
|
if (SeedX < cChunkDef::Width * 3 - 1)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2012-11-16 04:40:15 -05:00
|
|
|
if (SeedZ < cChunkDef::Width * 3 - 1)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2012-11-16 04:40:15 -05:00
|
|
|
if (SeedY < cChunkDef::Height - 1)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-02-08 15:55:21 -05:00
|
|
|
|
|
|
|
void cLightingThread::QueueChunkStay(cLightingChunkStay & a_ChunkStay)
|
|
|
|
{
|
|
|
|
// Move the ChunkStay from the Pending queue to the lighting queue.
|
|
|
|
{
|
|
|
|
cCSLock Lock(m_CS);
|
|
|
|
m_PendingQueue.remove(&a_ChunkStay);
|
|
|
|
m_Queue.push_back(&a_ChunkStay);
|
|
|
|
}
|
|
|
|
m_evtItemAdded.Set();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// cLightingThread::cLightingChunkStay:
|
|
|
|
|
|
|
|
cLightingThread::cLightingChunkStay::cLightingChunkStay(cLightingThread & a_LightingThread, int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter) :
|
|
|
|
m_LightingThread(a_LightingThread),
|
|
|
|
m_ChunkX(a_ChunkX),
|
|
|
|
m_ChunkZ(a_ChunkZ),
|
|
|
|
m_CallbackAfter(a_CallbackAfter)
|
|
|
|
{
|
|
|
|
Add(a_ChunkX + 1, a_ChunkZ + 1);
|
|
|
|
Add(a_ChunkX + 1, a_ChunkZ);
|
|
|
|
Add(a_ChunkX + 1, a_ChunkZ - 1);
|
|
|
|
Add(a_ChunkX, a_ChunkZ + 1);
|
|
|
|
Add(a_ChunkX, a_ChunkZ);
|
|
|
|
Add(a_ChunkX, a_ChunkZ - 1);
|
|
|
|
Add(a_ChunkX - 1, a_ChunkZ + 1);
|
|
|
|
Add(a_ChunkX - 1, a_ChunkZ);
|
|
|
|
Add(a_ChunkX - 1, a_ChunkZ - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-02-10 16:47:10 -05:00
|
|
|
bool cLightingThread::cLightingChunkStay::OnAllChunksAvailable(void)
|
2014-02-08 15:55:21 -05:00
|
|
|
{
|
|
|
|
m_LightingThread.QueueChunkStay(*this);
|
2014-02-10 16:47:10 -05:00
|
|
|
|
|
|
|
// Keep the ChunkStay alive:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cLightingThread::cLightingChunkStay::OnDisabled(void)
|
|
|
|
{
|
|
|
|
// Nothing needed in this callback
|
2014-02-08 15:55:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|