Not working geometry shader.

master
Nicole Collings 2020-06-05 16:47:43 -07:00
parent aa624464e0
commit e51de627a2
16 changed files with 204 additions and 167 deletions

View File

@ -4,15 +4,15 @@ layout (location = 0) out vec4 gPosition;
layout (location = 1) out vec4 gNormal;
layout (location = 2) out vec4 gSpecular;
in vec2 texCoords;
in vec2 blendMaskCoords;
in vec3 blend;
in vec3 fragPos;
in vec3 normal;
in vec2 blendMaskCoords;
in vec2 texCoords;
in vec3 blend;
in vec3 light;
uniform float time;
uniform sampler2D tex;
void main() {

View File

@ -0,0 +1,123 @@
#version 330 core
#define TAU 6.28318530718
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
struct SHADER_MOD {
float type;
vec3 values;
};
struct VS_OUT {
vec3 pos;
vec3 normal;
vec2 blendMaskCoords;
vec2 texCoords;
vec3 blend;
vec3 light;
SHADER_MOD mod;
};
in VS_OUT gs_in[3];
out vec3 fragPos;
out vec3 normal;
out vec2 blendMaskCoords;
out vec2 texCoords;
out vec3 blend;
out vec3 light;
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;
uniform sampler2D swayTex;
uniform float time;
vec3 unpackFloat(float src) { return vec3(fract(src) * 2.0f - 1.0f, fract(src * 256.f) * 2.0f - 1.0f, fract(src * 65536.f) * 2.0f - 1.0f); }
vec4 rotateX(vec4 vertex, vec4 offset, float radians) {
vertex -= offset;
mat2 m = mat2(cos(radians), -sin(radians), sin(radians), cos(radians));
return vec4(m * vertex.zy, vertex.xw).zyxw + offset;
}
vec4 rotateY(vec4 vertex, vec4 offset, float radians) {
vertex -= offset;
mat2 m = mat2(cos(radians), -sin(radians), sin(radians), cos(radians));
return vec4(m * vertex.xz, vertex.yw).xzyw + offset;
}
vec4 rotateZ(vec4 vertex, vec4 offset, float radians) {
vertex -= offset;
mat2 m = mat2(cos(radians), -sin(radians), sin(radians), cos(radians));
return vec4(m * vertex.xy, vertex.zw).xyzw + offset;
}
void processVertex(VS_OUT v) {
vec4 pos = vec4(v.pos, 1);
vec4 nml = vec4(v.normal, 1);
// switch (int(v.mod.type)) {
// default: break;
// case 1: { // Rotate X
// vec4 origin = vec4(round(unpackFloat(v.mod.values.x) * 8 + 8) + 0.5, 1);
// pos = rotateX(pos, origin, time * TAU * v.mod.values.y);
// nml = rotateX(nml, vec4(0), time * TAU * v.mod.values.y);
// break;
// }
// case 2: { // Rotate Y
// vec4 origin = vec4(round(unpackFloat(v.mod.values.x) * 8 + 8) + 0.5, 1);
// pos = rotateY(pos, origin, time * TAU * v.mod.values.y);
// nml = rotateY(nml, vec4(0), time * TAU * v.mod.values.y);
// break;
// }
// case 3: { // Rotate Z
// vec4 origin = vec4(round(unpackFloat(v.mod.values.x) * 8 + 8) + 0.5, 1);
// pos = rotateZ(pos, origin, time * TAU * v.mod.values.y);
// nml = rotateZ(nml, vec4(0), time * TAU * v.mod.values.y);
// break;
// }
// case 4: { //Sway Grounded
// vec4 origin = vec4(round(unpackFloat(v.mod.values.x) * 8 + 8), 1);
// vec3 bsp = vec3(pos - origin);
// vec3 worldPos = (model * pos).xyz;
// if (bsp.x*bsp.y*bsp.z != 0 && bsp.x*bsp.y*bsp.z != 1) {
// vec3 sway = (texture(swayTex, worldPos.xz * (worldPos.y / 16.f) / 16.f).xyz - .5f) * vec3(v.mod.values.y, v.mod.values.y / 2, v.mod.values.y);
// pos += vec4(sway, 0);
// }
// break;
// }
// case 5: { //Sway Full Block
// vec3 worldPos = (model * pos).xyz;
// vec3 sway = (texture(swayTex, worldPos.xz * (worldPos.y / 16.f) / 16.f).xyz - .5f) * vec3(v.mod.values.y, v.mod.values.y / 2, v.mod.values.y);
// pos += vec4(sway, 0);
// break;
// }
// }
vec4 worldPos = model * pos;
// worldPos.y -= pow(length(view * worldPos * 0.025) - 0, 2);
// worldPos.y += sin(time + (worldPos.x + worldPos.z) / 10) * clamp(length(view * worldPos * 0.1) - 1, 0, 2.5);
gl_Position = projection * view * worldPos;
fragPos = (view * worldPos).xyz;
normal = nml.xyz;
// Passthrough
blendMaskCoords = v.blendMaskCoords;
texCoords = v.texCoords;
blend = v.blend;
light = v.light;
EmitVertex();
}
void main() {
for (int i = 0; i < 3; i++) processVertex(gs_in[i]);
}

View File

@ -1,112 +1,52 @@
#version 330 core
#define TAU 6.28318530718
#define MAX_BLOCKLIGHT 31
#define MAX_SUNLIGHT 15
layout (location = 0) in vec3 aPos;
layout (location = 4) in float aNormal;
layout (location = 3) in vec2 aBlendMaskCoords;
layout (location = 1) in vec2 aTexCoords;
layout (location = 2) in vec3 aBlend;
layout (location = 3) in vec2 aBlendMaskCoords;
layout (location = 4) in float aNormal;
layout (location = 5) in vec4 aLight;
layout (location = 6) in float aShaderMod;
layout (location = 7) in vec3 aModValues;
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;
struct SHADER_MOD {
float type;
vec3 values;
};
uniform sampler2D swayTex;
uniform float time;
out VS_OUT {
vec3 pos;
vec3 normal;
out vec2 texCoords;
out vec2 blendMaskCoords;
out vec3 blend;
out vec3 fragPos;
out vec3 normal;
out vec3 light;
vec2 blendMaskCoords;
vec2 texCoords;
vec3 blend;
vec3 light;
vec3 unpackFloat(float src) {
return vec3(fract(src) * 2.0f - 1.0f, fract(src * 256.f) * 2.0f - 1.0f, fract(src * 65536.f) * 2.0f - 1.0f);
}
SHADER_MOD mod;
} vs_out;
vec4 rotateX(vec4 vertex, vec4 offset, float radians) {
vertex -= offset;
mat2 m = mat2(cos(radians), -sin(radians), sin(radians), cos(radians));
return vec4(m * vertex.zy, vertex.xw).zyxw + offset;
}
vec4 rotateY(vec4 vertex, vec4 offset, float radians) {
vertex -= offset;
mat2 m = mat2(cos(radians), -sin(radians), sin(radians), cos(radians));
return vec4(m * vertex.xz, vertex.yw).xzyw + offset;
}
vec4 rotateZ(vec4 vertex, vec4 offset, float radians) {
vertex -= offset;
mat2 m = mat2(cos(radians), -sin(radians), sin(radians), cos(radians));
return vec4(m * vertex.xy, vertex.zw).xyzw + offset;
}
vec3 unpackFloat(float src) { return vec3(fract(src) * 2.0f - 1.0f, fract(src * 256.f) * 2.0f - 1.0f, fract(src * 65536.f) * 2.0f - 1.0f); }
void main() {
vec4 pos = vec4(aPos, 1);
vec4 nml = vec4(unpackFloat(aNormal), 1);
switch (int(aShaderMod)) {
default: break;
case 1: { // Rotate X
vec4 origin = vec4(round(unpackFloat(aModValues.x) * 8 + 8) + 0.5, 1);
pos = rotateX(pos, origin, time * TAU * aModValues.y);
nml = rotateX(nml, vec4(0), time * TAU * aModValues.y);
break;
}
case 2: { // Rotate Y
vec4 origin = vec4(round(unpackFloat(aModValues.x) * 8 + 8) + 0.5, 1);
pos = rotateY(pos, origin, time * TAU * aModValues.y);
nml = rotateY(nml, vec4(0), time * TAU * aModValues.y);
break;
}
case 3: { // Rotate Z
vec4 origin = vec4(round(unpackFloat(aModValues.x) * 8 + 8) + 0.5, 1);
pos = rotateZ(pos, origin, time * TAU * aModValues.y);
nml = rotateZ(nml, vec4(0), time * TAU * aModValues.y);
break;
}
case 4: { //Sway Grounded
vec4 origin = vec4(round(unpackFloat(aModValues.x) * 8 + 8), 1);
vec3 bsp = vec3(pos - origin);
vec3 worldPos = (model * pos).xyz;
if (bsp.x*bsp.y*bsp.z != 0 && bsp.x*bsp.y*bsp.z != 1) {
vec3 sway = (texture(swayTex, worldPos.xz * (worldPos.y / 16.f) / 16.f).xyz - .5f) * vec3(aModValues.y, aModValues.y / 2, aModValues.y);
pos += vec4(sway, 0);
}
break;
}
case 5: { //Sway Full Block
vec3 worldPos = (model * pos).xyz;
vec3 sway = (texture(swayTex, worldPos.xz * (worldPos.y / 16.f) / 16.f).xyz - .5f) * vec3(aModValues.y, aModValues.y / 2, aModValues.y);
pos += vec4(sway, 0);
break;
}
}
float sunlightIntensity = 1;
// float sunlightIntensity = aLight.w * clamp(sin(time / 2.5) + 0.25, 0, 1) / MAX_SUNLIGHT;
vec3 blockLightColor = (aLight.xyz / MAX_BLOCKLIGHT) * vec3(1 + sunlightIntensity / 4);
vec3 sunlightColor = clamp(sunlightIntensity * 1.25 * vec3(1, 1, 1) * (aLight.w / 15.0), 0, 1);
vec3 resultantLight = vec3(max(sunlightColor.x, blockLightColor.x), max(sunlightColor.y, blockLightColor.y), max(sunlightColor.z, blockLightColor.z));
vec3 light = vec3(max(sunlightColor.x, blockLightColor.x), max(sunlightColor.y, blockLightColor.y), max(sunlightColor.z, blockLightColor.z));
vec4 worldPos = model * pos;
// worldPos.y -= pow(length(view * worldPos * 0.125) - 0.125, 2);
worldPos.y -= pow(length(view * worldPos * 0.025) - 0, 2);
vs_out.pos = aPos;
vs_out.normal = unpackFloat(aNormal);
fragPos = (view * worldPos).xyz;
texCoords = aTexCoords;
blendMaskCoords = aBlendMaskCoords;
blend = aBlend;
normal = nml.xyz;
light = resultantLight;
gl_Position = projection * view * worldPos;
vs_out.blendMaskCoords = aBlendMaskCoords;
vs_out.texCoords = aTexCoords;
vs_out.blend = aBlend;
vs_out.light = light;
vs_out.mod = SHADER_MOD(aShaderMod, aModValues);
}

View File

@ -44,10 +44,8 @@ void FarMapGen::buildDensityMap(FarMapJob *job, glm::ivec3 mbPos) {
int sampleWid = 64 / job->downScale;
float factor = (job->downScale == 2 ? 0.5f : job->downScale == 4 ? 1 : 2);
glm::ivec3 lp;
for (int m = 0; m < pow(sampleWid, 3); m++) {
Vec::indAssignVec(m, lp, sampleWid);
glm::ivec3 lp = Space::Block::fromIndex(m);
job->density[m] = (volume.get(glm::vec3(lp) * factor) + heightmap.get(glm::vec3(lp) * factor)) - (static_cast<float>(lp.y) * factor + mbPos.y * 64);
}
}

View File

@ -229,10 +229,6 @@ void MapGen::generateSunlight(MapGen::chunk_partials_map &chunks, glm::ivec3 mbP
unsigned int ind = Space::Block::index(b);
if (defs.blockFromId(chunk->getBlock(ind)).lightPropagates) {
chunk->setSunlight(ind, 15);
// const static std::array<glm::ivec3, 4> checks {
// glm::ivec3 {-1, 0, 0}, glm::ivec3 {1, 0, 0}, glm::ivec3 {0, 0, -1}, glm::ivec3 {0, 0, 1}};
sunlightQueue.emplace(ind, chunk);
}
else {

View File

@ -44,7 +44,7 @@ private:
// Generate sunlight on the mapgen threads to speed up perf
void generateSunlight(chunk_partials_map& chunks, glm::ivec3 mbPos);
bool containsWorldPos(BlockChunk *chunk, glm::ivec3 pos);
static bool containsWorldPos(BlockChunk *chunk, glm::ivec3 pos);
void propogateSunlightNodes(chunk_partials_map& chunks, std::queue<SunlightNode>& queue);
// Place block in the `chunks` array, creates a partial if necessary.

View File

@ -23,7 +23,7 @@ Renderer::Renderer(glm::ivec2 win) :
ssao.createFromFile("./assets/shader/post/passThrough.vs", "./assets/shader/post/ssaoCalc.fs");
blur.createFromFile("./assets/shader/post/passThrough.vs", "./assets/shader/post/ssaoBlur.fs");
light.createFromFile("./assets/shader/post/passThrough.vs", "./assets/shader/post/deferredLighting.fs");
world.createFromFile("./assets/shader/world/deferredGeometryWorld.vs", "./assets/shader/world/deferredGeometryWorld.fs");
world.createFromFile("./assets/shader/world/deferredGeometryWorld.vs", "./assets/shader/world/deferredGeometryWorld.fs", "./assets/shader/world/deferredGeometryWorld.gs");
entity.createFromFile("./assets/shader/world/deferredGeometryEntity.vs", "./assets/shader/world/deferredGeometryEntity.fs");
guiShader = Shader();

View File

@ -10,19 +10,13 @@
#include "../../../util/Log.h"
void Shader::createFromString(std::string& vertexSource, std::string& fragmentSource) {
void Shader::createFromString(std::string& vertexSource, std::string& fragmentSource, const std::string& geoSource) {
compileShader(vertexSource, fragmentSource);
}
void Shader::createFromFile(const std::string& vertexFile, const std::string& fragmentFile) {
this->vertexFile = vertexFile;
this->fragmentFile = fragmentFile;
std::string vertexSource = readFile(vertexFile);
std::string fragmentSource = readFile(fragmentFile);
compileShader(vertexSource, fragmentSource);
void Shader::createFromFile(const std::string& vertexFile, const std::string& fragmentFile, const std::string& geoFile) {
compileShader(readFile(vertexFile), readFile(fragmentFile),
geoFile == "" ? "" : readFile(geoFile));
postCreate();
}
@ -58,41 +52,41 @@ GLint Shader::get(const std::string &name) {
}
void Shader::set(int loc, unsigned int val) {
crashIfInactive();
checkActive();
glUniform1ui(loc, val);
}
void Shader::set(int loc, int val) {
crashIfInactive();
checkActive();
glUniform1i(loc, val);
}
void Shader::set(int loc, float val) {
crashIfInactive();
checkActive();
glUniform1f(loc, val);
}
void Shader::set(int loc, glm::vec3 val) {
crashIfInactive();
checkActive();
glUniform3f(loc, val.x, val.y, val.z);
}
void Shader::set(int loc, glm::vec4 val) {
crashIfInactive();
checkActive();
glUniform4f(loc, val.x, val.y, val.z, val.w);
}
void Shader::set(int loc, glm::mat4 val) {
crashIfInactive();
checkActive();
glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(val));
}
void Shader::setArr(int loc, unsigned int count, glm::mat4 &start) {
crashIfInactive();
checkActive();
glUniformMatrix4fv(loc, count, GL_FALSE, glm::value_ptr(start));
}
void Shader::compileShader(const std::string& vertexSource, const std::string& fragmentSource) {
void Shader::compileShader(const std::string& vertexSource, const std::string& fragmentSource, const std::string& geoSource) {
shaderID = glCreateProgram();
if (!shaderID) {
@ -102,6 +96,7 @@ void Shader::compileShader(const std::string& vertexSource, const std::string& f
addShader(shaderID, vertexSource, GL_VERTEX_SHADER);
addShader(shaderID, fragmentSource, GL_FRAGMENT_SHADER);
if (geoSource != "") addShader(shaderID, geoSource, GL_GEOMETRY_SHADER);
GLint result = 0;
GLchar eLog[1024] = { 0 };
@ -140,11 +135,11 @@ void Shader::addShader(GLuint program, const std::string& shaderCode, GLenum sha
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
if (!result) {
std::string& shaderName = (shaderType == GL_VERTEX_SHADER) ? vertexFile : fragmentFile;
std::string shaderTypeName = (shaderType == GL_VERTEX_SHADER) ? "vertex" : (shaderType == GL_FRAGMENT_SHADER) ? "fragment" : "geometry";
glGetShaderInfoLog(shader, sizeof(eLog), nullptr, eLog);
std::cout << Log::err << "-- Error compiling '" << shaderName << "' --\n" << eLog << Log::endl
<< Log::err << shaderCode << std::endl;
std::cout << Log::err << "Error compiling the " << shaderTypeName << " shader:\n" << eLog
<< Log::endl << Log::err << shaderCode << std::endl;
return;
}
@ -156,16 +151,11 @@ Shader::~Shader() {
}
void Shader::cleanup() {
if (shaderID != 0) {
glDeleteProgram(shaderID);
}
if (shaderID != 0) glDeleteProgram(shaderID);
}
void Shader::crashIfInactive() {
void Shader::checkActive() {
int cProgram;
glGetIntegerv(GL_CURRENT_PROGRAM, &cProgram);
if (cProgram != shaderID) {
std::cout << Log::err << "-- Attempted action on inactive shader! --" << Log::endl;
throw std::exception();
}
if (cProgram != shaderID) throw std::runtime_error("Attempted to set a uniform on an inactive shader!");
}

View File

@ -11,8 +11,8 @@
class Shader {
public:
void createFromString(std::string& vertexSource, std::string& fragmentSource);
void createFromFile(const std::string& vertexFile, const std::string& fragmentFile);
void createFromString(std::string& vertexSource, std::string& fragmentSource, const std::string& geoSource = "");
void createFromFile(const std::string& vertexFile, const std::string& fragmentFile, const std::string& geoFile = "");
virtual void postCreate() {};
@ -35,15 +35,13 @@ public:
~Shader();
private:
std::string readFile(const std::string& fileLocation);
static std::string readFile(const std::string& fileLocation);
void compileShader(const std::string& vertexSource, const std::string& fragmentSource);
void addShader(unsigned int program, const std::string& shaderCode, GLenum shaderType);
void compileShader(const std::string& vertexSource, const std::string& fragmentSource, const std::string& geoSource = "");
static void addShader(unsigned int program, const std::string& shaderCode, GLenum shaderType);
void crashIfInactive();
void checkActive();
unsigned int shaderID = 0;
std::string vertexFile {};
std::string fragmentFile {};
};

View File

@ -4,6 +4,7 @@
#include "MeshGenStream.h"
#include "graph/ChunkMeshGenerator.h"
#include "../../../world/LocalDimension.h"
MeshGenStream::MeshGenStream(ClientGame& game, LocalDimension &dimension) :
@ -45,7 +46,7 @@ std::vector<ChunkMeshDetails*> MeshGenStream::update() {
}
if (!queuedTasks.empty()) {
glm::vec3 pos = *queuedTasks.begin();
glm::ivec3 pos = *queuedTasks.begin();
queuedTasks.erase(queuedTasks.begin());
queuedMap.erase(pos);
@ -56,7 +57,7 @@ std::vector<ChunkMeshDetails*> MeshGenStream::update() {
u.thisChunk = std::shared_ptr<BlockChunk>(chunk);
int ind = 0;
for (glm::vec3 dir : Vec::cardinalVectors) {
for (const glm::ivec3& dir : Vec::adj) {
std::shared_ptr<BlockChunk> adjacent = dimension.getChunk(pos + dir);
u.adjacentChunks[ind++] = std::shared_ptr<BlockChunk>(adjacent);
if (adjacent == nullptr) goto breakAddTask;
@ -101,11 +102,11 @@ bool MeshGenStream::spaceInQueue() {
return queuedTasks.size() < TOTAL_QUEUE_SIZE;
}
bool MeshGenStream::isQueued(glm::vec3 pos) {
return (bool) queuedMap.count(pos);
bool MeshGenStream::isQueued(glm::ivec3 pos) {
return static_cast<bool>(queuedMap.count(pos));
}
bool MeshGenStream::tryToQueue(glm::vec3 pos) {
bool MeshGenStream::tryToQueue(glm::ivec3 pos) {
unsigned long sizeOfQueue = queuedTasks.size();
if (sizeOfQueue < TOTAL_QUEUE_SIZE && !queuedMap.count(pos)) {

View File

@ -5,15 +5,15 @@
#pragma once
#include <glm/vec3.hpp>
#include <thread>
#include <glm/vec3.hpp>
#include <unordered_set>
#include "ChunkMeshDetails.h"
#include "graph/ChunkMeshGenerator.h"
#include "../../../world/chunk/BlockChunk.h"
#include "../../../def/LocalDefinitionAtlas.h"
#include "../../../util/Vec.h"
#include "../../../def/ClientGame.h"
#include "../../../def/gen/NoiseSample.h"
#include "../../../world/chunk/BlockChunk.h"
class LocalDimension;
@ -27,10 +27,10 @@ public:
~MeshGenStream();
bool spaceInQueue();
bool isQueued(glm::vec3 pos);
bool isQueued(glm::ivec3 pos);
//Attempt to add `pos` to the pre-thread queue.
//Will return a boolean stating if there is more space left in the queue.
bool tryToQueue(glm::vec3 pos);
bool tryToQueue(glm::ivec3 pos);
//Will return a vector of MeshDetails pointers containing finished meshes.
//Frees up the threads and starts new tasks.
@ -64,7 +64,7 @@ private:
ClientGame& game;
std::array<NoiseSample, 3> noiseSampler;
std::vector<glm::vec3> queuedTasks;
std::vector<glm::ivec3> queuedTasks;
std::unordered_set<glm::vec3, Vec::vec3> queuedMap;
};

View File

@ -60,16 +60,9 @@ void ServerWorld::update(double delta) {
generatedChunks = static_cast<int>(finished->size());
for (auto& mb : *finished) {
Timer t("finishing mapblock");
for (const auto& chunk : mb.chunks) {
dimension.setChunk(chunk);
// dimension.createSunlight(chunk->pos);
}
// dimension.propogateLight();
for (const auto& chunk : mb.chunks) dimension.setChunk(chunk);
dimension.calculateEdgeLight(mb.pos);
dimension.getMapBlock(mb.pos)->generated = true;
t.printElapsedMs();
unsigned long long mapBlockIntegrity = dimension.getMapBlockIntegrity(mb.pos);
for (auto& client : clientList.clients) {

View File

@ -109,7 +109,7 @@ namespace Space {
// Get the index of a Chunk within its MapBlock from its local or world position.
static inline unsigned int index(const glm::ivec3& vec) {
glm::ivec3 local = Chunk::relative::toMapBlock(vec);
return static_cast<unsigned int>(local.x + MAPBLOCK_SIZE * (local.y + MAPBLOCK_SIZE * local.z));
return static_cast<unsigned int>(local.x + MAPBLOCK_SIZE * (local.z + MAPBLOCK_SIZE * local.y));
}
// Return a local vector of an chunk within its mapblock.

View File

@ -23,11 +23,7 @@ namespace Vec {
}
};
const static std::array<glm::ivec3, 6> cardinalVectors = {
glm::ivec3 {1, 0, 0}, glm::ivec3 {-1, 0, 0},
glm::ivec3 {0, 1, 0}, glm::ivec3 {0, -1, 0},
glm::ivec3 {0, 0, 1}, glm::ivec3 {0, 0, -1}
};
const static std::array<glm::ivec3, 6> adj = { glm::ivec3 {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1} };
static inline void indAssignVec(int ind, glm::ivec3& vec, unsigned int wid = 16) {
vec.y = ind / (wid * wid);

View File

@ -7,5 +7,6 @@ zepha.register_block("zeus:default:cobblestone", {
pick = 3
},
yields = "zeus:default:cobblestone",
light_source = { 31, 0, 0 },
light_source = { 5, 20, 20 },
light_propagates = true,
})

View File

@ -7,6 +7,7 @@ zepha.register_block("zeus:default:dirt", {
shovel = 1,
pick = 2
},
light_source = { 0, 16, 31 },
yields = "zeus:default:dirt"
yields = "zeus:default:dirt",
light_source = { 30, 5, 5 },
light_propagates = true,
})