summaryrefslogblamecommitdiffstats
path: root/src/LightingThread.cpp
blob: 6f51e173ca6255c694a86cd13f3e237a4f7e586d (plain) (tree)
1
2
3
4
5
6
7
8
9






                                                                                                    
                     
                      
                  




 
 
                                                                                                                         


                                 
                                                                         
         

                                                                  
 

                                                                                 
                                                                                                     
         
 
       




                                                                                                                                                        
                                           
                                        

         





   
                                                                                



                                        
                        

















                                             
                                                       
                          
 








                                
                                 
                                  






               
                                                                                                                  
 
                                                                      
 

                                                                                                     
         




                                                                        
         

 
                                                                                                       
         



                                                                                                                   
         
                                                   








                                            
                                     







                                   
                                  
         


                                               

                                           
                                                         
                 
 
                                                       
                 

                                                                                   
                 
 

                                                         
                         




                                                                                                               
                         
                         
 




                                                       





 
                                                             
 



                                                                                       
 
                                                                               
 
          


                                                                               
                                                                                                           
         
         
                                                              
                 


                                                                               
                                                                                                                                                       

                                                                              
                                                                                                                              





                                                                                 
                 
                           
         
            
 
                                                                         
 



                                                                                      


                                                                                                              
         
         
                                                              
                 

                                                                        
                                                                                                                                                  



                                                                               

                                                                                                                                                  



                                                                           
                 


                           
         
            
 





                                                                                                         
 

                                                                                     
                 














                                                                                                                 





 
                                                                                                                    
 

                                                                  
 
                                                                            
         
                                                                                           
                 

                                                                                                   
 
                                                                           

                 

                                





 
                                                                                                                        
 

                                                                      
 
                                                                             
         

                                                                                      
 


                                                             

                 

                                





 
                                                                                                                                    
 
                                           
         

                                                      
 
                                                                                  






         
                                                                                                                                                                             
 
                                                                        
 



                                                                                                                   
         
 




                                                                                                                   
 




                                                                                                                                  
 




                                                                                                                                  
 
                                                
         

                                                                                                                                                     
 



                                                                                                                                                     





 
                                                                                                                                                                                                     
 

                                                                                             
         
                                                                    





         
 
                                                                                                     
 
                                                                     
         

                                                                                                                                  
         





 
                                                                                

                                       
                                                                                                                                                                               
                                                    

                           

                                                                         
 
                                





 
                                                                    
 




































                                                                                                                                  
 









                                                          
                    




 

// LightingThread.cpp

// Implements the cLightingThread class representing the thread that processes requests for lighting

#include "Globals.h"
#include "LightingThread.h"
#include "ChunkMap.h"
#include "ChunkStay.h"
#include "World.h"






/** Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]: */
class cReader :
	public cChunkDataCallback
{
	virtual void ChunkData(const cChunkData & a_ChunkBuffer) override
	{
		a_ChunkBuffer.CopyBlockTypes(m_BlockTypes.data());
	}

	virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override
	{
		std::copy(std::begin(*a_Heightmap), std::end(*a_Heightmap), std::begin(m_HeightMap));
	}

public:

	cLightingThread::BlockDataArray & m_BlockTypes;  // 3x3 chunks of block types, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
	cLightingThread::HeightDataArray & m_HeightMap;  // 3x3 chunks of height map,  organized as a single XZY blob of data (instead of 3x3 XZY blobs)

	cReader(cLightingThread::BlockDataArray & a_BlockTypes, cLightingThread::HeightDataArray & a_HeightMap) :
		m_BlockTypes(a_BlockTypes),
		m_HeightMap(a_HeightMap)
	{
	}
} ;





////////////////////////////////////////////////////////////////////////////////
// cLightingThread:

cLightingThread::cLightingThread(void) :
	super("cLightingThread"),
	m_World(nullptr)
{
}





cLightingThread::~cLightingThread()
{
	Stop();
}





bool cLightingThread::Start(cWorld * a_World)
{
	ASSERT(m_World == nullptr);  // Not started yet
	m_World = a_World;

	return super::Start();
}





void cLightingThread::Stop(void)
{
	m_ShouldTerminate = true;
	m_evtItemDataLoaded.Set();
	Wait();
}





void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, std::unique_ptr<cChunkCoordCallback> a_CallbackAfter)
{
	ASSERT(m_World != nullptr);  // Did you call Start() properly?

	// If the chunk is already lit, skip it:
	if (m_World->IsChunkValid(a_ChunkX, a_ChunkZ) && m_World->IsChunkLighted(a_ChunkX, a_ChunkZ))
	{
		if (a_CallbackAfter != nullptr)
		{
			a_CallbackAfter->Call(a_ChunkX, a_ChunkZ, true);
		}
		return;
	}


	auto ChunkStay = new cLightingChunkStay(*this, a_ChunkX, a_ChunkZ, std::move(a_CallbackAfter));
	{
		// 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
		cCSLock Lock(m_CS);
		m_PendingQueue.emplace_back(ChunkStay);
	}
	ChunkStay->Enable(*m_World->GetChunkMap());
}





size_t cLightingThread::GetQueueLength(void)
{
	cCSLock Lock(m_CS);
	return m_PendingQueue.size();
}





void cLightingThread::Execute(void)
{
	while (!m_ShouldTerminate)
	{
		m_evtItemDataLoaded.Wait();

		decltype(m_Queue) QueuedChunks;
		{
			cCSLock Lock(m_CS);
			std::swap(m_Queue, QueuedChunks);
		}

		for (const auto & Entry : QueuedChunks)
		{
			Entry->m_BlockTypes = cpp14::make_unique<BlockDataArray>();
			Entry->m_HeightMap = cpp14::make_unique<HeightDataArray>();
		}

		m_World->QueueTask(
			[&QueuedChunks](cWorld & a_World)
			{
				for (const auto & Entry : QueuedChunks)
				{
					cReader Reader(*Entry->m_BlockTypes, *Entry->m_HeightMap);
					VERIFY(a_World.GetChunkData(Entry->m_ChunkX, Entry->m_ChunkZ, Reader));
				}
			}
		).wait();

		for (const auto & Entry : QueuedChunks)
		{
			LightChunk(*Entry);
		}
	}  // while (!m_ShouldTerminate)
}





void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
{
	std::unique_ptr<BlockDataArray>
		BlockLight{ cpp14::make_unique<decltype(BlockLight)::element_type>() },
		SkyLight{ cpp14::make_unique<decltype(BlockLight)::element_type>() }
	;

	CalcLight(a_Item, PrepareBlockLight(a_Item, *BlockLight), *BlockLight);

	/*
	// DEBUG: Save chunk data with highlighted seeds for visual inspection:
	cFile f4;
	if (
		f4.Open(Printf("Chunk_%d_%d_seeds.grab", a_Item.m_ChunkX, a_Item.m_ChunkZ), cFile::fmWrite)
	)
	{
		for (int z = 0; z < cChunkDef::Width * 3; z++)
		{
			for (int y = cChunkDef::Height / 2; y >= 0; y--)
			{
				unsigned char Seeds     [cChunkDef::Width * 3];
				memcpy(Seeds, m_BlockTypes + y * cLightingChunkStay::BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3);
				for (int x = 0; x < cChunkDef::Width * 3; x++)
				{
					if (m_IsSeed1[y * cLightingChunkStay::BlocksPerYLayer + z * cChunkDef::Width * 3 + x])
					{
						Seeds[x] = E_BLOCK_DIAMOND_BLOCK;
					}
				}
				f4.Write(Seeds, cChunkDef::Width * 3);
			}
		}
		f4.Close();
	}
	//*/

	CalcLight(a_Item, PrepareSkyLight(a_Item, *SkyLight), *SkyLight);

	/*
	// DEBUG: Save XY slices of the chunk data and lighting for visual inspection:
	cFile f1, f2, f3;
	if (
		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)
	)
	{
		for (int z = 0; z < cChunkDef::Width * 3; z++)
		{
			for (int y = cChunkDef::Height / 2; y >= 0; y--)
			{
				f1.Write(m_BlockTypes + y * cLightingChunkStay::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 * cLightingChunkStay::BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4;
					BlockLight[x] = m_BlockLight[y * cLightingChunkStay::BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4;
				}
				f2.Write(SkyLight,   cChunkDef::Width * 3);
				f3.Write(BlockLight, cChunkDef::Width * 3);
			}
		}
		f1.Close();
		f2.Close();
		f3.Close();
	}
	//*/

	std::shared_ptr<BlockNibbles>
		CompressedBlockLight{ std::make_shared<decltype(CompressedBlockLight)::element_type>() },
		CompressedSkyLight{ std::make_shared<decltype(CompressedSkyLight)::element_type>() }
	;
	CompressLight(*BlockLight, *CompressedBlockLight);
	CompressLight(*SkyLight, *CompressedSkyLight);

	m_World->QueueTask(
		[&a_Item, CompressedBlockLight, CompressedSkyLight](cWorld & a_World)
		{
			a_World.ChunkLighted(
				a_Item.m_ChunkX,
				a_Item.m_ChunkZ,
				reinterpret_cast<const cChunkDef::BlockNibbles &>(*CompressedBlockLight->data()),
				reinterpret_cast<const cChunkDef::BlockNibbles &>(*CompressedSkyLight->data())
			);

			if (a_Item.m_CallbackAfter != nullptr)
			{
				a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ, true);
			}

			a_Item.Disable();
		}
	);
}





std::vector<size_t> cLightingThread::PrepareSkyLight(const cLightingChunkStay & a_Item, BlockDataArray & a_SkyLight)
{
	std::vector<size_t> IndicesToProcess;
	IndicesToProcess.reserve(a_Item.m_HeightMap->size() * 40);

	for (size_t Index = 0; Index != a_Item.m_HeightMap->size(); ++Index)
	{
		for (int Y = cChunkDef::Height - 1; Y >= (*a_Item.m_HeightMap)[Index]; --Y)
		{
			auto HeightAdjustedIndex = Index + Y * cChunkDef::Width * cChunkDef::Width;
			a_SkyLight[HeightAdjustedIndex] = 15;

			IndicesToProcess.emplace_back(HeightAdjustedIndex);
		}
	}

	return IndicesToProcess;
}





std::vector<size_t> cLightingThread::PrepareBlockLight(const cLightingChunkStay & a_Item, BlockDataArray & a_BlockLight)
{
	std::vector<size_t> IndicesToProcess;
	IndicesToProcess.reserve(cChunkDef::Width * cChunkDef::Width);

	for (size_t Index = 0; Index != a_Item.m_BlockTypes->size(); ++Index)
	{
		auto Light = cBlockInfo::GetLightValue((*a_Item.m_BlockTypes)[Index]);
		a_BlockLight[Index] = Light;

		if (Light > 1)
		{
			IndicesToProcess.emplace_back(Index);
		}
	}

	return IndicesToProcess;
}





void cLightingThread::CalcLight(const cLightingChunkStay & a_Item, std::vector<size_t> a_IndicesToProcess, BlockDataArray & a_Light)
{
	while (!a_IndicesToProcess.empty())
	{
		auto Back = a_IndicesToProcess.back();
		a_IndicesToProcess.pop_back();

		DiscoverLightAdjacents(a_IndicesToProcess, a_Item, a_Light, Back);
	}
}





void cLightingThread::DiscoverLightAdjacents(std::vector<size_t> & a_IndicesToProcess, const cLightingChunkStay & a_Item, BlockDataArray & a_Light, size_t a_OriginatorIndex)
{
	auto Position = cChunkDef::IndexToCoordinate(a_OriginatorIndex);

	if (Position.x % cChunkDef::Width != 15)
	{
		// TODO: update neighbouring chunk
		UpdateLightAdjacent(a_IndicesToProcess, a_Item, a_Light, a_OriginatorIndex, a_OriginatorIndex + 1);
	}

	if (Position.x % cChunkDef::Width != 0)
	{
		// TODO: update neighbouring chunk
		UpdateLightAdjacent(a_IndicesToProcess, a_Item, a_Light, a_OriginatorIndex, a_OriginatorIndex - 1);
	}

	if (Position.z % cChunkDef::Width != 15)
	{
		// TODO: update neighbouring chunk
		UpdateLightAdjacent(a_IndicesToProcess, a_Item, a_Light, a_OriginatorIndex, a_OriginatorIndex + cChunkDef::Width);
	}

	if (Position.z % cChunkDef::Width != 0)
	{
		// TODO: update neighbouring chunk
		UpdateLightAdjacent(a_IndicesToProcess, a_Item, a_Light, a_OriginatorIndex, a_OriginatorIndex - cChunkDef::Width);
	}

	if (Position.y != cChunkDef::Height - 1)
	{
		UpdateLightAdjacent(a_IndicesToProcess, a_Item, a_Light, a_OriginatorIndex, a_OriginatorIndex + cChunkDef::Width * cChunkDef::Width);
	}

	if (Position.y != 0)
	{
		UpdateLightAdjacent(a_IndicesToProcess, a_Item, a_Light, a_OriginatorIndex, a_OriginatorIndex - cChunkDef::Width * cChunkDef::Width);
	}
}





void cLightingThread::UpdateLightAdjacent(std::vector<size_t> & a_IndicesToProcess, const cLightingChunkStay & a_Item, BlockDataArray & a_Light, size_t a_OriginatorIndex, size_t a_DestinationIndex)
{
	// Assertions for index validity occur within PropagateLightToAdjacent
	if (PropagateLightToAdjacent(a_Item, a_Light, a_OriginatorIndex, a_DestinationIndex))
	{
		a_IndicesToProcess.emplace_back(a_DestinationIndex);
	}
}





void cLightingThread::CompressLight(const BlockDataArray & a_LightArray, BlockNibbles & a_ChunkLight)
{
	for (size_t Index = 0; Index != a_ChunkLight.size(); ++Index)
	{
		auto AdjustedIndex = Index * 2;
		a_ChunkLight[Index] = a_LightArray[AdjustedIndex] | static_cast<NIBBLETYPE>(a_LightArray[AdjustedIndex + 1] << 4);
	}
}





////////////////////////////////////////////////////////////////////////////////
// cLightingThread::cLightingChunkStay:

cLightingThread::cLightingChunkStay::cLightingChunkStay(cLightingThread & a_LightingThread, int a_ChunkX, int a_ChunkZ, std::unique_ptr<cChunkCoordCallback> a_CallbackAfter) :
	m_CallbackAfter(std::move(a_CallbackAfter)),
	m_ChunkX(a_ChunkX),
	m_ChunkZ(a_ChunkZ),
	m_MaxHeight(0),  // Required by cReader to be initialised to zero
	m_LightingThread(a_LightingThread)
{
	Add(a_ChunkX, a_ChunkZ);
}





bool cLightingThread::cLightingChunkStay::OnAllChunksAvailable(void)
{
	if (m_LightingThread.m_ShouldTerminate)
	{
		return false;
	}

	ASSERT(m_LightingThread.m_World->IsChunkValid(m_ChunkX, m_ChunkZ));

	// If the chunk is already lit, skip it:
	if (m_LightingThread.m_World->IsChunkLighted(m_ChunkX, m_ChunkZ))
	{
		if (m_CallbackAfter != nullptr)
		{
			m_CallbackAfter->Call(m_ChunkX, m_ChunkZ, true);
		}


		{
			cCSLock Lock(m_LightingThread.m_CS);
			m_LightingThread.m_PendingQueue.erase(
				std::remove(m_LightingThread.m_PendingQueue.begin(), m_LightingThread.m_PendingQueue.end(), this),
				m_LightingThread.m_PendingQueue.end()
			);
		}

		return true;
	}

	{
		cCSLock Lock(m_LightingThread.m_CS);
		m_LightingThread.m_PendingQueue.erase(
			std::remove(m_LightingThread.m_PendingQueue.begin(), m_LightingThread.m_PendingQueue.end(), this),
			m_LightingThread.m_PendingQueue.end()
		);
		m_LightingThread.m_Queue.emplace_back(this);
	}

	m_LightingThread.m_evtItemDataLoaded.Set();

	// Keep the ChunkStay alive:
	return false;
}





void cLightingThread::cLightingChunkStay::OnDisabled(void)
{
	delete this;
}