Browse Source

Functioning light cascading

testing
totallyfake 1 year ago
parent
commit
d6518efa9e
6 changed files with 267 additions and 14 deletions
  1. 2
    0
      CMakeLists.txt
  2. 25
    1
      include/chunk.h
  3. 227
    7
      src/chunk.cpp
  4. 3
    3
      src/graphics/voxelmodel.cpp
  5. 8
    1
      src/scenes/testscene.cpp
  6. 2
    2
      src/world.cpp

+ 2
- 0
CMakeLists.txt View File

@@ -6,6 +6,8 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-O2")
set(CMAKE_C_FLAGS "-fPIC")
set(CMAKE_EXPORT_COMPILE_COMMANDS on)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall")
add_executable(vtk ${SOURCES})

set(CMAKE_THREAD_PREFER_PTHREAD TRUE)

+ 25
- 1
include/chunk.h View File

@@ -4,6 +4,7 @@
#include "util/mobileatomic.h"

#include <glm/glm.hpp>
#include <queue>

namespace vtk {

@@ -37,6 +38,10 @@ public:
unsigned getVoxelType(const unsigned& x, const unsigned& y, const unsigned& z);

void rebuildLighting();
void addLight(const glm::ivec3& pos, const unsigned short& light);
void removeLight(const glm::ivec3& pos);


glm::ivec3 getWorldCoords(const int& x, const int& y, const int& z);
unsigned getLightLevel(const glm::ivec3& pos);
unsigned short getLightPacked(const glm::ivec3& pos);
@@ -47,9 +52,21 @@ public:
glm::ivec3 getPos();
World& getWorld();

//has no protections for out of bounds
unsigned short fastGetLightPacked(const glm::ivec3& pos);

protected:
typedef std::tuple<short, Chunk*> LightIndexTup;

void propogateLight();
void propogateLightTask();

//conversion funcs for directly accessing data and lighting
short vecToIndex(const glm::ivec3& pos);
glm::ivec3 indexToVec(const short& index);

//index, mask, Chunk* tuple
typedef std::tuple<short, unsigned short, Chunk*> LightIndexTup;

void setQueuedForMeshRebuild(const bool& rebuild = true);
bool isQueuedForMeshRebuild();

@@ -57,8 +74,15 @@ protected:
std::array<util::MobileAtomic<unsigned short>, 4096> mLighting;
World& mLinkedWorld;
glm::ivec3 mPos;

//queue for lighting voxels with BFS
std::queue<LightIndexTup> mLightBFSQueue;
//queue for removing lights with BFS
std::queue<LightIndexTup> mDarkBFSQueue;

std::atomic<bool> mLoaded;
std::atomic<bool> mQueuedForMeshRebuild;
std::atomic<bool> mPropogating;
};

}

+ 227
- 7
src/chunk.cpp View File

@@ -37,6 +37,8 @@ unsigned Chunk::breakVoxel(const glm::ivec3& pos) {
//update heightmap
mLinkedWorld.getHeightMap(glm::ivec2(mPos.x, mPos.z))
->unblockHeight(glm::ivec3(pos.x, pos.y + mPos.y * 16, pos.z));

removeLight(pos);
mLinkedWorld.queueChunkUpdate(mPos);

//update the neightboring chunks if voxel lies along border
@@ -77,6 +79,12 @@ bool Chunk::placeVoxel(const glm::ivec3& pos, const unsigned& type) {
mLinkedWorld.getHeightMap(glm::ivec2(mPos.x, mPos.z))
->blockHeight(glm::ivec3(pos.x, pos.y + mPos.y * 16, pos.z));

unsigned short emission = mLinkedWorld.voxelInfo.getEmission(type);
removeLight(pos);
if (emission != 0) {
addLight(pos, emission);
}
mLinkedWorld.queueChunkUpdate(mPos, true);


@@ -110,6 +118,8 @@ bool Chunk::placeVoxel(const glm::ivec3& pos, const unsigned& type) {
return true;
}



bool Chunk::isVoxelSolid(const int& x, const int& y, const int& z) {
if (x < 0 || x > 15 ||
y < 0 || y > 15 ||
@@ -158,6 +168,7 @@ unsigned Chunk::getVoxelType(const unsigned& x, const unsigned& y, const unsigne

void Chunk::rebuildLighting() {
std::queue<LightIndexTup> sunBFSQueue;
unsigned short mask2 = 0x000F;
//FIRST PASS, block out all solid blocks
for (short i = 0; i < 4096; ++i) {
glm::ivec3 pos(i % 16, (i / 16) % 16,(i / 256)); // get current coord
@@ -171,10 +182,10 @@ void Chunk::rebuildLighting() {
{
lightVal = lightVal | 0xF; //max out the sun lighting
}
mLighting[i].store(lightVal);
mLighting[i].store(lightVal, std::memory_order_release);

if ((lightVal & 0xF) != 0) {
sunBFSQueue.push(std::make_tuple(i,this));
sunBFSQueue.push(std::make_tuple(i,mask2,this));
}

}
@@ -182,8 +193,9 @@ void Chunk::rebuildLighting() {
//iterate through sunlight
while (!sunBFSQueue.empty()) {
short index;
unsigned short mask;
Chunk* chunk;
std::tie(index, chunk) = sunBFSQueue.front();
std::tie(index, mask, chunk) = sunBFSQueue.front();
glm::ivec3 pos( index % 16,
(index / 16) % 16,
index / 256);
@@ -207,10 +219,10 @@ void Chunk::rebuildLighting() {
if (straightDown) {
nChunk->setLightPacked(nPos, (newLight & 0xFFF0) | 0xF);
} else {
nChunk->setLightPacked(nPos, (newLight & 0xFFF0) | light - 1);
nChunk->setLightPacked(nPos, ((newLight & 0xFFF0) | light) - 1);
}
if (light - 1 > 1) {
sunBFSQueue.push(std::make_tuple((short)(nPos.x + 16 * (nPos.y + 16 * nPos.z)), nChunk));
sunBFSQueue.push(std::make_tuple((short)(nPos.x + 16 * (nPos.y + 16 * nPos.z)), mask, nChunk));
}
}
}
@@ -228,6 +240,37 @@ void Chunk::rebuildLighting() {
}
}

void Chunk::addLight(const glm::ivec3 &pos, const unsigned short &light) {
setLightPacked(pos, light);
//check channels
if (light >> 12 > 0) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0xF000, this));
}
if (((light >> 8) & 0xF) > 0) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x0F00, this));
}
if (((light >> 4) & 0xF) > 0) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x00F0, this));
}
if ((light & 0xF) > 0) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x000F, this));
}
propogateLight();
}

void Chunk::removeLight(const glm::ivec3 &pos) {
//propogate all 4 channels
/*mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0xF000, this));
mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x0F00, this));
mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x00F0, this));
*/
mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x000F, this)); //sunlight
mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0xF000, this)); //R
mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x0F00, this)); //G
mDarkBFSQueue.push(std::make_tuple(vecToIndex(pos), (unsigned short)0x00F0, this)); //B
propogateLight();
}

glm::ivec3 Chunk::getWorldCoords(const int& x, const int& y, const int& z) {
return glm::ivec3(mPos.x * 16 + x,
mPos.y * 16 + y,
@@ -255,12 +298,12 @@ unsigned short Chunk::getLightPacked(const glm::ivec3 &pos) {
int index = pos.x + 16 * (pos.y + 16 * pos.z);

return mLighting[index].load();
return mLighting[index].load(std::memory_order_consume);
}

void Chunk::setLightPacked(const glm::ivec3& pos, const unsigned short& light) {
int index = pos.x + 16 * (pos.y + 16 * pos.z);
mLighting[index].store(light);
mLighting[index].store(light, std::memory_order_release);
}

HeightMap* Chunk::getHeightMap() {
@@ -280,6 +323,11 @@ World& Chunk::getWorld() {
return mLinkedWorld;
}

unsigned short Chunk::fastGetLightPacked(const glm::ivec3 &pos) {
int index = vecToIndex(pos);
//std::cout << mPos.x << "," << mPos.y << "," << mPos.z << std::endl;
return mLighting[index].load(std::memory_order_acquire);
}
void Chunk::setQueuedForMeshRebuild(const bool& rebuild) {
mQueuedForMeshRebuild = rebuild;
}
@@ -288,4 +336,176 @@ bool Chunk::isQueuedForMeshRebuild() {
return mQueuedForMeshRebuild.load();
}

void Chunk::propogateLight() {
if (mPropogating.load(std::memory_order_acq_rel)) return;
mPropogating.store(true, std::memory_order_acq_rel);

propogateLightTask();
}

void Chunk::propogateLightTask() {
while(!mDarkBFSQueue.empty()) {

//TODO Mask channels properly
//load front of queue
short index;
unsigned short mask;
Chunk* chunk;
std::tie(index, mask, chunk) = mDarkBFSQueue.front();

mDarkBFSQueue.pop();

glm::ivec3 pos = indexToVec(index);
unsigned short light = chunk->fastGetLightPacked(pos);
chunk->setLightPacked(pos, light & (~ mask)); //mask out correct channel

auto propogateDark =
[&](glm::ivec3 nPos, Chunk* nChunk)
{
auto checkPos = localPosToLocalPos(nChunk->getPos(), nPos);
nChunk = mLinkedWorld.getChunk(checkPos.first);
nPos = checkPos.second;
//return early if chunk doesn't exist or voxel is solid
if (nChunk == nullptr || nChunk->isVoxelSolid(nPos)) return;
auto newLight = nChunk->getLightPacked(nPos);
if ((newLight & mask) == 0) return;
//sunglight (to handle special case with straight down
if (mask == 0x000F && ((newLight & 0xF) < (light & 0xF) || (pos.y + chunk->getPos().y * 16 > nPos.y + nChunk->getPos().y * 16 && (light & 0xF) == 0xF))) {
mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
} else if (mask == 0x000F) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}

// Red
if (mask == 0xF000 && ((newLight >> 12) < (light >> 12))) {
mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
} else if (mask == 0xF000) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}
// Green
if (mask == 0x0F00 && ((newLight & mask) >> 8 < (light & mask) >> 8)) {
mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
} else if (mask == 0x0F00) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}
// Blue
if (mask == 0x00F0 && ((newLight & mask) >> 4 < (light & mask) >> 4)) {
mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
} else if (mask == 0x00F0) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}
/*
if (mask == 0xF000 && newLight >> 12 < (light >> 12) + 1) {
if (((newLight & mask) >> 12) < ((light & mask) >> 12)) {
mDarkBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
} else {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}
}
*/
};

//visit neighbors
propogateDark(glm::ivec3(pos.x-1, pos.y, pos.z), chunk);
propogateDark(glm::ivec3(pos.x+1, pos.y, pos.z), chunk);
propogateDark(glm::ivec3(pos.x, pos.y-1, pos.z), chunk);
propogateDark(glm::ivec3(pos.x, pos.y+1, pos.z), chunk);
propogateDark(glm::ivec3(pos.x, pos.y, pos.z-1), chunk);
propogateDark(glm::ivec3(pos.x, pos.y, pos.z+1), chunk);
}

while(!mLightBFSQueue.empty()) {

//TODO Mask channels properly
//load front of queue
short index;
unsigned short mask;
Chunk* chunk;
std::tie(index, mask, chunk) = mLightBFSQueue.front();

mLightBFSQueue.pop();

glm::ivec3 pos = indexToVec(index);
unsigned short light = chunk->fastGetLightPacked(pos);
//std::cout << light << std::endl;
//std::cout << pos.x << "," << pos.y << "," << pos.z << std::endl;

auto propogateLight =
[&](glm::ivec3 nPos, Chunk* nChunk)
{
auto checkPos = localPosToLocalPos(nChunk->getPos(), nPos);
nChunk = mLinkedWorld.getChunk(checkPos.first);
nPos = checkPos.second;
//return early if chunk doesn't exist or voxel is solid
if (nChunk == nullptr || nChunk->isVoxelSolid(nPos)) return;
auto newLight = nChunk->fastGetLightPacked(nPos);

//red channel
if (mask == 0xF000 && (light >> 12) > (newLight >> 12) + 1) {
setLightPacked(nPos, ((newLight & (~ mask)) | (((light >> 12) - 1)) << 12)); //set new light level with proper masking
if (((light & mask) >> 12) - 1 > 1) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}
}
//green channel
if (mask == 0x0F00 && ((light >> 8) & 0xF) > ((newLight >> 8) & 0xF) + 1) {
setLightPacked(nPos, ((newLight & (~ mask)) | ((((light >> 8) & 0xF) - 1)) << 8)); //set new light level with proper masking
if (((light & mask) >> 8) - 1 > 1) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}
}
//blue channel
if (mask == 0x00F0 && ((light >> 4) & 0xF) > ((newLight >> 4) & 0xF) + 1) {
setLightPacked(nPos, ((newLight & (~ mask)) | ((((light >> 4) & 0xF) - 1)) << 4)); //set new light level with proper masking
if (((light & mask) >> 4) - 1 > 1) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}
}
//sunglight channel
if (mask == 0x000F && (light & 0xF) > (newLight & 0xF) + 1) {
//handle straight down propogation
if (pos.y + chunk->getPos().y * 16 > nPos.y + nChunk->getPos().y * 16 && (light & 0xF) == 0xF) {
nChunk->setLightPacked(nPos, newLight | 0xF);
} else {
nChunk->setLightPacked(nPos, (((newLight & (~ mask)) | (light & 0xF)) - 1));
}
if ((light & mask) - 1 > 1) {
mLightBFSQueue.push(std::make_tuple(vecToIndex(nPos), mask, nChunk));
}
}
};

//visit neighbors
propogateLight(glm::ivec3(pos.x-1, pos.y, pos.z), chunk);
propogateLight(glm::ivec3(pos.x+1, pos.y, pos.z), chunk);
propogateLight(glm::ivec3(pos.x, pos.y-1, pos.z), chunk);
propogateLight(glm::ivec3(pos.x, pos.y+1, pos.z), chunk);
propogateLight(glm::ivec3(pos.x, pos.y, pos.z-1), chunk);
propogateLight(glm::ivec3(pos.x, pos.y, pos.z+1), chunk);
}

//release the the task
mPropogating.store(false);
//restart it if any light updates slipped through the cracks
if (!mDarkBFSQueue.empty() ) {//|| !mLightBFSQueue.empty()) {
propogateLight();
}
}

short Chunk::vecToIndex(const glm::ivec3& pos) {
return pos.x + 16 * (pos.y + 16 * pos.z);
}

glm::ivec3 Chunk::indexToVec(const short& index) {
return glm::ivec3(index % 16,
(index / 16) % 16,
index / 256);

}

}

+ 3
- 3
src/graphics/voxelmodel.cpp View File

@@ -133,15 +133,15 @@ void VoxelModel::getFaceLighting(std::vector<unsigned short>& lighting, const Fa
((b >> 12) & 0xF) +
((c >> 12) & 0xF) +
((d >> 12) & 0xF)) / 4;
avg = (avg << 8) | (((a >> 8) & 0xF) +
avg = (avg << 4) | (((a >> 8) & 0xF) +
((b >> 8) & 0xF) +
((c >> 8) & 0xF) +
((d >> 8) & 0xF)) / 4;
avg = (avg << 8) | (((a >> 4) & 0xF) +
avg = (avg << 4) | (((a >> 4) & 0xF) +
((b >> 4) & 0xF) +
((c >> 4) & 0xF) +
((d >> 4) & 0xF)) / 4;
avg = (avg << 8) | ((a & 0xF) +
avg = (avg << 4) | ((a & 0xF) +
(b & 0xF) +
(c & 0xF) +
(d & 0xF)) / 4;

+ 8
- 1
src/scenes/testscene.cpp View File

@@ -45,6 +45,7 @@ void TestScene::init() {
tiles.updateTextureAt(0, "res/stone.png");
tiles.updateTextureAt(1, "res/dirt.png");
tiles.updateTextureAt(2, "res/grass.png");
tiles.updateTextureAt(3, "res/test.png");

//shaders
shaders = LoadShaders("res/shaders/voxelvert.vert.glsl", "res/shaders/voxelfrag.frag.glsl");
@@ -86,6 +87,7 @@ void TestScene::init() {
handler.setAction("Delete Voxel", conf->getValue<std::string>("controls.bindings.action.delvoxel", "Mouse Right"));
handler.setAction("Select Type 1", conf->getValue<std::string>("controls.bindings.typesel.type1", "1" ));
handler.setAction("Select Type 2", conf->getValue<std::string>("controls.bindings.typesel.type2", "2" ));
handler.setAction("Select Type 3", conf->getValue<std::string>("controls.bindings.typesel.type3", "3" ));
handler.setAction("Toggle Noclip", conf->getValue<std::string>("controls.bindings.noclip", "V"));

//set signals for handler
@@ -96,14 +98,18 @@ void TestScene::init() {
world.voxelInfo.setAllTextureIndexes(1, 0);
world.voxelInfo.setAllTextureIndexes(2, 1);
world.voxelInfo.setTextureIndex(2, FaceDirection::TOP, 2);
world.voxelInfo.setAllTextureIndexes(3, 3);

world.voxelInfo.setTransparent(0, true);
world.voxelInfo.setTransparent(1, false);
world.voxelInfo.setTransparent(2, false);
world.voxelInfo.setTransparent(3, false);


world.voxelInfo.setEmission(0, 0);
world.voxelInfo.setEmission(1, 0xF000);
world.voxelInfo.setEmission(1, 0);
world.voxelInfo.setEmission(2, 0);
world.voxelInfo.setEmission(3, 0xFFF0);

std::cout << std::endl;

@@ -137,6 +143,7 @@ void TestScene::update(const float& dTime) {

if (handler.isActionDown("Select Type 1")) voxelType = 1;
if (handler.isActionDown("Select Type 2")) voxelType = 2;
if (handler.isActionDown("Select Type 3")) voxelType = 3;
if (!mNoclipDebounce && handler.isActionDown("Toggle Noclip")) {
mNoclipDebounce = true;
mNoclip = !mNoclip;

+ 2
- 2
src/world.cpp View File

@@ -115,6 +115,7 @@ bool World::generateChunk(const int& x, const int& y, const int& z) {
auto chunkMade = makeChunk(x,y,z);
if (chunkMade != nullptr) {
terrain.generateChunk(chunkMade);
chunkMade->rebuildLighting();
//queue this chunk for geometry update
queueChunkUpdate(x,y,z);

@@ -216,7 +217,6 @@ void World::update() {
glm::ivec2 hm_pos;
while (mMeshUpdateQueueSoon.try_dequeue(pos)) {
getChunk(pos)->rebuildLighting();
ChunkMesh* mesh;
mChunkMeshes.find(pos, mesh);
if (mesh) {
@@ -226,7 +226,7 @@ void World::update() {
}
while (mMeshUpdateQueue.try_dequeue(pos)) {
getChunk(pos)->rebuildLighting();
//getChunk(pos)->rebuildLighting();
ChunkMesh* mesh;
mChunkMeshes.find(pos, mesh);
if (mesh) {

Loading…
Cancel
Save