commit
f6de40ca88
17
res/content/base/blocks/wooden_door.json
Normal file
17
res/content/base/blocks/wooden_door.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"material": "base:wood",
|
||||
"texture-faces": [
|
||||
"wooden_door_side",
|
||||
"wooden_door_side",
|
||||
"wooden_door_top",
|
||||
"wooden_door_top",
|
||||
"wooden_door",
|
||||
"wooden_door"
|
||||
],
|
||||
"light-passing": true,
|
||||
"sky-light-passing": true,
|
||||
"size": [1, 2, 1],
|
||||
"rotation": "pane",
|
||||
"model": "aabb",
|
||||
"hitbox": [0.0, 0.0, 0.8, 1.0, 2.0, 0.2]
|
||||
}
|
||||
@ -25,6 +25,7 @@
|
||||
"pane",
|
||||
"pipe",
|
||||
"lightbulb",
|
||||
"torch"
|
||||
"torch",
|
||||
"wooden_door"
|
||||
]
|
||||
}
|
||||
6
res/content/base/preload.json
Normal file
6
res/content/base/preload.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"sounds": [
|
||||
"blocks/door_open",
|
||||
"blocks/door_close"
|
||||
]
|
||||
}
|
||||
13
res/content/base/scripts/wooden_door.lua
Normal file
13
res/content/base/scripts/wooden_door.lua
Normal file
@ -0,0 +1,13 @@
|
||||
function on_interact(x, y, z)
|
||||
local inc = 1
|
||||
if block.get_user_bits(x, y, z, 0, 1) > 0 then
|
||||
inc = 3
|
||||
block.set_user_bits(x, y, z, 0, 1, 0)
|
||||
audio.play_sound("blocks/door_close", x+0.5, y+1, z+0.5, 1, 1)
|
||||
else
|
||||
block.set_user_bits(x, y, z, 0, 1, 1)
|
||||
audio.play_sound("blocks/door_open", x+0.5, y+1, z+0.5, 1, 1)
|
||||
end
|
||||
block.set_rotation(x, y, z, (block.get_rotation(x, y, z) + inc) % 4)
|
||||
return true
|
||||
end
|
||||
BIN
res/content/base/sounds/blocks/door_close.ogg
Normal file
BIN
res/content/base/sounds/blocks/door_close.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/blocks/door_open.ogg
Normal file
BIN
res/content/base/sounds/blocks/door_open.ogg
Normal file
Binary file not shown.
BIN
res/content/base/textures/blocks/wooden_door.png
Normal file
BIN
res/content/base/textures/blocks/wooden_door.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
BIN
res/content/base/textures/blocks/wooden_door_side.png
Normal file
BIN
res/content/base/textures/blocks/wooden_door_side.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
BIN
res/content/base/textures/blocks/wooden_door_top.png
Normal file
BIN
res/content/base/textures/blocks/wooden_door_top.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
@ -62,6 +62,7 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
||||
def.rt.id = blockDefsIndices.size();
|
||||
def.rt.emissive = *reinterpret_cast<uint32_t*>(def.emission);
|
||||
def.rt.solid = def.model == BlockModel::block;
|
||||
def.rt.extended = def.size.x > 1 || def.size.y > 1 || def.size.z > 1;
|
||||
|
||||
if (def.rotatable) {
|
||||
for (uint i = 0; i < BlockRotProfile::MAX_COUNT; i++) {
|
||||
|
||||
@ -137,16 +137,14 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat
|
||||
def.model = BlockModel::custom;
|
||||
if (root->has("model-primitives")) {
|
||||
loadCustomBlockModel(def, root->map("model-primitives"));
|
||||
}
|
||||
else {
|
||||
std::cerr << "ERROR occured while block "
|
||||
<< name << " parsed: no \"model-primitives\" found" << std::endl;
|
||||
} else {
|
||||
logger.error() << name << ": no 'model-primitives' found";
|
||||
}
|
||||
}
|
||||
else if (model == "X") def.model = BlockModel::xsprite;
|
||||
else if (model == "none") def.model = BlockModel::none;
|
||||
else {
|
||||
std::cerr << "unknown model " << model << std::endl;
|
||||
logger.error() << "unknown model " << model;
|
||||
def.model = BlockModel::none;
|
||||
}
|
||||
|
||||
@ -156,12 +154,12 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat
|
||||
std::string profile = "none";
|
||||
root->str("rotation", profile);
|
||||
def.rotatable = profile != "none";
|
||||
if (profile == "pipe") {
|
||||
if (profile == BlockRotProfile::PIPE_NAME) {
|
||||
def.rotations = BlockRotProfile::PIPE;
|
||||
} else if (profile == "pane") {
|
||||
} else if (profile == BlockRotProfile::PANE_NAME) {
|
||||
def.rotations = BlockRotProfile::PANE;
|
||||
} else if (profile != "none") {
|
||||
std::cerr << "unknown rotation profile " << profile << std::endl;
|
||||
logger.error() << "unknown rotation profile " << profile;
|
||||
def.rotatable = false;
|
||||
}
|
||||
|
||||
@ -175,29 +173,37 @@ void ContentLoader::loadBlock(Block& def, const std::string& name, const fs::pat
|
||||
def.hitboxes[i].b = glm::vec3(box->num(3), box->num(4), box->num(5));
|
||||
def.hitboxes[i].b += def.hitboxes[i].a;
|
||||
}
|
||||
} else if (auto boxarr = root->list("hitbox")){
|
||||
AABB aabb;
|
||||
aabb.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2));
|
||||
aabb.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5));
|
||||
aabb.b += aabb.a;
|
||||
def.hitboxes = { aabb };
|
||||
} else if (!def.modelBoxes.empty()) {
|
||||
def.hitboxes = def.modelBoxes;
|
||||
} else {
|
||||
boxarr = root->list("hitbox");
|
||||
if (boxarr) {
|
||||
AABB aabb;
|
||||
aabb.a = glm::vec3(boxarr->num(0), boxarr->num(1), boxarr->num(2));
|
||||
aabb.b = glm::vec3(boxarr->num(3), boxarr->num(4), boxarr->num(5));
|
||||
aabb.b += aabb.a;
|
||||
def.hitboxes = { aabb };
|
||||
} else if (!def.modelBoxes.empty()) {
|
||||
def.hitboxes = def.modelBoxes;
|
||||
} else {
|
||||
def.hitboxes = { AABB() };
|
||||
}
|
||||
def.hitboxes = { AABB() };
|
||||
}
|
||||
|
||||
// block light emission [r, g, b] where r,g,b in range [0..15]
|
||||
auto emissionarr = root->list("emission");
|
||||
if (emissionarr) {
|
||||
if (auto emissionarr = root->list("emission")) {
|
||||
def.emission[0] = emissionarr->num(0);
|
||||
def.emission[1] = emissionarr->num(1);
|
||||
def.emission[2] = emissionarr->num(2);
|
||||
}
|
||||
|
||||
// block size
|
||||
if (auto sizearr = root->list("size")) {
|
||||
def.size.x = sizearr->num(0);
|
||||
def.size.y = sizearr->num(1);
|
||||
def.size.z = sizearr->num(2);
|
||||
if (def.model == BlockModel::block &&
|
||||
(def.size.x != 1 || def.size.y != 1 || def.size.z != 1)) {
|
||||
def.model = BlockModel::aabb;
|
||||
def.hitboxes = {AABB(def.size)};
|
||||
}
|
||||
}
|
||||
|
||||
// primitive properties
|
||||
root->flag("obstacle", def.obstacle);
|
||||
root->flag("replaceable", def.replaceable);
|
||||
@ -254,8 +260,8 @@ void ContentLoader::loadCustomBlockModel(Block& def, dynamic::Map* primitives) {
|
||||
/* Parse tetragon to points */
|
||||
auto tgonobj = modeltetragons->list(i);
|
||||
glm::vec3 p1(tgonobj->num(0), tgonobj->num(1), tgonobj->num(2)),
|
||||
xw(tgonobj->num(3), tgonobj->num(4), tgonobj->num(5)),
|
||||
yh(tgonobj->num(6), tgonobj->num(7), tgonobj->num(8));
|
||||
xw(tgonobj->num(3), tgonobj->num(4), tgonobj->num(5)),
|
||||
yh(tgonobj->num(6), tgonobj->num(7), tgonobj->num(8));
|
||||
def.modelExtraPoints.push_back(p1);
|
||||
def.modelExtraPoints.push_back(p1+xw);
|
||||
def.modelExtraPoints.push_back(p1+xw+yh);
|
||||
@ -279,7 +285,7 @@ void ContentLoader::loadItem(ItemDef& def, const std::string& name, const fs::pa
|
||||
} else if (iconTypeStr == "sprite") {
|
||||
def.iconType = item_icon_type::sprite;
|
||||
} else if (iconTypeStr.length()){
|
||||
std::cerr << "unknown icon type" << iconTypeStr << std::endl;
|
||||
logger.error() << name << ": unknown icon type" << iconTypeStr;
|
||||
}
|
||||
root->str("icon", def.icon);
|
||||
root->str("placing-block", def.placingBlock);
|
||||
|
||||
@ -75,20 +75,21 @@ std::shared_ptr<UINode> create_debug_panel(
|
||||
L" visible: "+std::to_wstring(level->chunks->visible);
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
const auto& vox = player->selection.vox;
|
||||
std::wstringstream stream;
|
||||
stream << "r:" << player->selectedVoxel.state.rotation << " s:"
|
||||
<< player->selectedVoxel.state.segment << " u:"
|
||||
<< std::bitset<8>(player->selectedVoxel.state.userbits);
|
||||
if (player->selectedVoxel.id == BLOCK_VOID) {
|
||||
stream << "r:" << vox.state.rotation << " s:"
|
||||
<< std::bitset<3>(vox.state.segment) << " u:"
|
||||
<< std::bitset<8>(vox.state.userbits);
|
||||
if (vox.id == BLOCK_VOID) {
|
||||
return std::wstring {L"block: -"};
|
||||
} else {
|
||||
return L"block: "+std::to_wstring(player->selectedVoxel.id)+
|
||||
return L"block: "+std::to_wstring(vox.id)+
|
||||
L" "+stream.str();
|
||||
}
|
||||
}));
|
||||
panel->add(create_label([=](){
|
||||
auto* indices = level->content->getIndices();
|
||||
if (auto def = indices->getBlockDef(player->selectedVoxel.id)) {
|
||||
if (auto def = indices->getBlockDef(player->selection.vox.id)) {
|
||||
return L"name: " + util::str2wstr_utf8(def->name);
|
||||
} else {
|
||||
return std::wstring {L"name: void"};
|
||||
|
||||
@ -27,6 +27,8 @@ BlocksRenderer::BlocksRenderer(
|
||||
const ContentGfxCache* cache,
|
||||
const EngineSettings* settings
|
||||
) : content(content),
|
||||
vertexBuffer(std::make_unique<float[]>(capacity)),
|
||||
indexBuffer(std::make_unique<int[]>(capacity)),
|
||||
vertexOffset(0),
|
||||
indexOffset(0),
|
||||
indexSize(0),
|
||||
@ -34,16 +36,14 @@ BlocksRenderer::BlocksRenderer(
|
||||
cache(cache),
|
||||
settings(settings)
|
||||
{
|
||||
vertexBuffer = new float[capacity];
|
||||
indexBuffer = new int[capacity];
|
||||
voxelsBuffer = new VoxelsVolume(CHUNK_W + 2, CHUNK_H, CHUNK_D + 2);
|
||||
voxelsBuffer = std::make_unique<VoxelsVolume>(
|
||||
CHUNK_W + voxelBufferPadding*2,
|
||||
CHUNK_H,
|
||||
CHUNK_D + voxelBufferPadding*2);
|
||||
blockDefsCache = content->getIndices()->getBlockDefs();
|
||||
}
|
||||
|
||||
BlocksRenderer::~BlocksRenderer() {
|
||||
delete voxelsBuffer;
|
||||
delete[] vertexBuffer;
|
||||
delete[] indexBuffer;
|
||||
}
|
||||
|
||||
/* Basic vertex add method */
|
||||
@ -60,10 +60,10 @@ void BlocksRenderer::vertex(const vec3& coord, float u, float v, const vec4& lig
|
||||
uint32_t integer;
|
||||
} compressed;
|
||||
|
||||
compressed.integer = (uint32_t(light.r * 255) & 0xff) << 24;
|
||||
compressed.integer |= (uint32_t(light.g * 255) & 0xff) << 16;
|
||||
compressed.integer |= (uint32_t(light.b * 255) & 0xff) << 8;
|
||||
compressed.integer |= (uint32_t(light.a * 255) & 0xff);
|
||||
compressed.integer = (static_cast<uint32_t>(light.r * 255) & 0xff) << 24;
|
||||
compressed.integer |= (static_cast<uint32_t>(light.g * 255) & 0xff) << 16;
|
||||
compressed.integer |= (static_cast<uint32_t>(light.b * 255) & 0xff) << 8;
|
||||
compressed.integer |= (static_cast<uint32_t>(light.a * 255) & 0xff);
|
||||
|
||||
vertexBuffer[vertexOffset++] = compressed.floating;
|
||||
}
|
||||
@ -78,15 +78,17 @@ void BlocksRenderer::index(int a, int b, int c, int d, int e, int f) {
|
||||
indexOffset += 4;
|
||||
}
|
||||
|
||||
/* Add face with precalculated lights */
|
||||
void BlocksRenderer::face(const vec3& coord,
|
||||
float w, float h, float d,
|
||||
const vec3& axisX,
|
||||
const vec3& axisY,
|
||||
const vec3& axisZ,
|
||||
const UVRegion& region,
|
||||
const vec4(&lights)[4],
|
||||
const vec4& tint) {
|
||||
/// @brief Add face with precalculated lights
|
||||
void BlocksRenderer::face(
|
||||
const vec3& coord,
|
||||
float w, float h, float d,
|
||||
const vec3& axisX,
|
||||
const vec3& axisY,
|
||||
const vec3& axisZ,
|
||||
const UVRegion& region,
|
||||
const vec4(&lights)[4],
|
||||
const vec4& tint
|
||||
) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
@ -102,23 +104,27 @@ void BlocksRenderer::face(const vec3& coord,
|
||||
index(0, 1, 3, 1, 2, 3);
|
||||
}
|
||||
|
||||
void BlocksRenderer::vertex(const vec3& coord,
|
||||
float u, float v,
|
||||
const vec4& tint,
|
||||
const vec3& axisX,
|
||||
const vec3& axisY,
|
||||
const vec3& axisZ) {
|
||||
void BlocksRenderer::vertex(
|
||||
const vec3& coord,
|
||||
float u, float v,
|
||||
const vec4& tint,
|
||||
const vec3& axisX,
|
||||
const vec3& axisY,
|
||||
const vec3& axisZ
|
||||
) {
|
||||
vec3 pos = coord+axisZ*0.5f+(axisX+axisY)*0.5f;
|
||||
vec4 light = pickSoftLight(ivec3(round(pos.x), round(pos.y), round(pos.z)), axisX, axisY);
|
||||
vertex(coord, u, v, light * tint);
|
||||
}
|
||||
|
||||
void BlocksRenderer::face(const vec3& coord,
|
||||
const vec3& X,
|
||||
const vec3& Y,
|
||||
const vec3& Z,
|
||||
const UVRegion& region,
|
||||
bool lights) {
|
||||
void BlocksRenderer::face(
|
||||
const vec3& coord,
|
||||
const vec3& X,
|
||||
const vec3& Y,
|
||||
const vec3& Z,
|
||||
const UVRegion& region,
|
||||
bool lights
|
||||
) {
|
||||
if (vertexOffset + BlocksRenderer::VERTEX_SIZE * 4 > capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
@ -148,14 +154,13 @@ void BlocksRenderer::face(const vec3& coord,
|
||||
index(0, 1, 2, 0, 2, 3);
|
||||
}
|
||||
|
||||
void BlocksRenderer::tetragonicFace(const vec3& coord, const vec3& p1,
|
||||
const vec3& p2, const vec3& p3, const vec3& p4,
|
||||
const vec3& X,
|
||||
const vec3& Y,
|
||||
const vec3& Z,
|
||||
const UVRegion& texreg,
|
||||
bool lights) {
|
||||
|
||||
void BlocksRenderer::tetragonicFace(
|
||||
const vec3& coord,
|
||||
const vec3& p1, const vec3& p2, const vec3& p3, const vec3& p4,
|
||||
const vec3& X, const vec3& Y, const vec3& Z,
|
||||
const UVRegion& texreg,
|
||||
bool lights
|
||||
) {
|
||||
const vec3 fp1 = (p1.x - 0.5f) * X + (p1.y - 0.5f) * Y + (p1.z - 0.5f) * Z;
|
||||
const vec3 fp2 = (p2.x - 0.5f) * X + (p2.y - 0.5f) * Y + (p2.z - 0.5f) * Z;
|
||||
const vec3 fp3 = (p3.x - 0.5f) * X + (p3.y - 0.5f) * Y + (p3.z - 0.5f) * Z;
|
||||
@ -182,17 +187,19 @@ void BlocksRenderer::tetragonicFace(const vec3& coord, const vec3& p1,
|
||||
index(0, 1, 3, 1, 2, 3);
|
||||
}
|
||||
|
||||
void BlocksRenderer::blockXSprite(int x, int y, int z,
|
||||
const vec3& size,
|
||||
const UVRegion& texface1,
|
||||
const UVRegion& texface2,
|
||||
float spread) {
|
||||
vec4 lights[]{
|
||||
pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}) };
|
||||
|
||||
void BlocksRenderer::blockXSprite(
|
||||
int x, int y, int z,
|
||||
const vec3& size,
|
||||
const UVRegion& texface1,
|
||||
const UVRegion& texface2,
|
||||
float spread
|
||||
) {
|
||||
vec4 lights[] {
|
||||
pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x + 1, y + 1, z}, {1, 0, 0}, {0, 1, 0}),
|
||||
pickSoftLight({x, y + 1, z}, {1, 0, 0}, {0, 1, 0})
|
||||
};
|
||||
int rand = ((x * z + y) ^ (z * y - x)) * (z + y);
|
||||
|
||||
float xs = ((float)(char)rand / 512) * spread;
|
||||
@ -218,7 +225,7 @@ void BlocksRenderer::blockXSprite(int x, int y, int z,
|
||||
|
||||
// HINT: texture faces order: {east, west, bottom, top, south, north}
|
||||
|
||||
/* AABB blocks render method */
|
||||
/// @brief AABB blocks render method
|
||||
void BlocksRenderer::blockAABB(
|
||||
const ivec3& icoord,
|
||||
const UVRegion(&texfaces)[6],
|
||||
@ -259,8 +266,9 @@ void BlocksRenderer::blockAABB(
|
||||
face(coord, Z*size.z, Y*size.y, -X*size.x, texfaces[0], lights); // east
|
||||
}
|
||||
|
||||
void BlocksRenderer::blockCustomModel(const ivec3& icoord,
|
||||
const Block* block, ubyte rotation, bool lights) {
|
||||
void BlocksRenderer::blockCustomModel(
|
||||
const ivec3& icoord, const Block* block, ubyte rotation, bool lights
|
||||
) {
|
||||
vec3 X(1, 0, 0);
|
||||
vec3 Y(0, 1, 0);
|
||||
vec3 Z(0, 0, 1);
|
||||
@ -347,8 +355,9 @@ bool BlocksRenderer::isOpen(int x, int y, int z, ubyte group) const {
|
||||
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
|
||||
y,
|
||||
chunk->z * CHUNK_D + z);
|
||||
if (id == BLOCK_VOID)
|
||||
if (id == BLOCK_VOID) {
|
||||
return false;
|
||||
}
|
||||
const Block& block = *blockDefsCache[id];
|
||||
if ((block.drawGroup != group && block.lightPassing) || !block.rt.solid) {
|
||||
return true;
|
||||
@ -360,8 +369,9 @@ bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
|
||||
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
|
||||
y,
|
||||
chunk->z * CHUNK_D + z);
|
||||
if (id == BLOCK_VOID)
|
||||
if (id == BLOCK_VOID) {
|
||||
return false;
|
||||
}
|
||||
const Block& block = *blockDefsCache[id];
|
||||
if (block.lightPassing) {
|
||||
return true;
|
||||
@ -371,15 +381,13 @@ bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
|
||||
|
||||
vec4 BlocksRenderer::pickLight(int x, int y, int z) const {
|
||||
if (isOpenForLight(x, y, z)) {
|
||||
light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x,
|
||||
y,
|
||||
light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, y,
|
||||
chunk->z * CHUNK_D + z);
|
||||
return vec4(Lightmap::extract(light, 0) / 15.0f,
|
||||
Lightmap::extract(light, 1) / 15.0f,
|
||||
Lightmap::extract(light, 2) / 15.0f,
|
||||
Lightmap::extract(light, 3) / 15.0f);
|
||||
}
|
||||
else {
|
||||
Lightmap::extract(light, 1) / 15.0f,
|
||||
Lightmap::extract(light, 2) / 15.0f,
|
||||
Lightmap::extract(light, 3) / 15.0f);
|
||||
} else {
|
||||
return vec4(0.0f);
|
||||
}
|
||||
}
|
||||
@ -391,17 +399,20 @@ vec4 BlocksRenderer::pickLight(const ivec3& coord) const {
|
||||
vec4 BlocksRenderer::pickSoftLight(const ivec3& coord,
|
||||
const ivec3& right,
|
||||
const ivec3& up) const {
|
||||
return (
|
||||
pickLight(coord) +
|
||||
pickLight(coord - right) +
|
||||
pickLight(coord - right - up) +
|
||||
pickLight(coord - up)) * 0.25f;
|
||||
return (pickLight(coord) +
|
||||
pickLight(coord - right) +
|
||||
pickLight(coord - right - up) +
|
||||
pickLight(coord - up)) * 0.25f;
|
||||
}
|
||||
|
||||
vec4 BlocksRenderer::pickSoftLight(float x, float y, float z,
|
||||
const ivec3& right,
|
||||
const ivec3& up) const {
|
||||
return pickSoftLight({int(round(x)), int(round(y)), int(round(z))}, right, up);
|
||||
const ivec3& right,
|
||||
const ivec3& up) const {
|
||||
return pickSoftLight({
|
||||
static_cast<int>(round(x)),
|
||||
static_cast<int>(round(y)),
|
||||
static_cast<int>(round(z))},
|
||||
right, up);
|
||||
}
|
||||
|
||||
void BlocksRenderer::render(const voxel* voxels) {
|
||||
@ -411,48 +422,55 @@ void BlocksRenderer::render(const voxel* voxels) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
const voxel& vox = voxels[i];
|
||||
blockid_t id = vox.id;
|
||||
blockstate state = vox.state;
|
||||
const Block& def = *blockDefsCache[id];
|
||||
if (id == 0 || def.drawGroup != drawGroup)
|
||||
if (id == 0 || def.drawGroup != drawGroup || state.segment) {
|
||||
continue;
|
||||
const UVRegion texfaces[6]{ cache->getRegion(id, 0),
|
||||
cache->getRegion(id, 1),
|
||||
cache->getRegion(id, 2),
|
||||
cache->getRegion(id, 3),
|
||||
cache->getRegion(id, 4),
|
||||
cache->getRegion(id, 5)};
|
||||
}
|
||||
const UVRegion texfaces[6] {
|
||||
cache->getRegion(id, 0),
|
||||
cache->getRegion(id, 1),
|
||||
cache->getRegion(id, 2),
|
||||
cache->getRegion(id, 3),
|
||||
cache->getRegion(id, 4),
|
||||
cache->getRegion(id, 5)
|
||||
};
|
||||
int x = i % CHUNK_W;
|
||||
int y = i / (CHUNK_D * CHUNK_W);
|
||||
int z = (i / CHUNK_D) % CHUNK_W;
|
||||
switch (def.model) {
|
||||
case BlockModel::block:
|
||||
blockCube(x, y, z, texfaces, &def, vox.state, !def.rt.emissive);
|
||||
break;
|
||||
case BlockModel::xsprite: {
|
||||
blockXSprite(x, y, z, vec3(1.0f),
|
||||
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
||||
break;
|
||||
case BlockModel::block:
|
||||
blockCube(x, y, z, texfaces, &def, vox.state, !def.rt.emissive);
|
||||
break;
|
||||
case BlockModel::xsprite: {
|
||||
blockXSprite(x, y, z, vec3(1.0f),
|
||||
texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
|
||||
break;
|
||||
}
|
||||
case BlockModel::aabb: {
|
||||
blockAABB(ivec3(x,y,z), texfaces, &def, vox.state.rotation, !def.rt.emissive);
|
||||
break;
|
||||
}
|
||||
case BlockModel::custom: {
|
||||
blockCustomModel(ivec3(x, y, z), &def, vox.state.rotation, !def.rt.emissive);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case BlockModel::aabb: {
|
||||
blockAABB(ivec3(x,y,z), texfaces, &def, vox.state.rotation, !def.rt.emissive);
|
||||
break;
|
||||
}
|
||||
case BlockModel::custom: {
|
||||
blockCustomModel(ivec3(x, y, z), &def, vox.state.rotation, !def.rt.emissive);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (overflow)
|
||||
if (overflow) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
this->chunk = chunk;
|
||||
voxelsBuffer->setPosition(chunk->x * CHUNK_W - 1, 0, chunk->z * CHUNK_D - 1);
|
||||
chunks->getVoxels(voxelsBuffer, settings->graphics.backlight.get());
|
||||
voxelsBuffer->setPosition(
|
||||
chunk->x * CHUNK_W - voxelBufferPadding, 0,
|
||||
chunk->z * CHUNK_D - voxelBufferPadding);
|
||||
chunks->getVoxels(voxelsBuffer.get(), settings->graphics.backlight.get());
|
||||
overflow = false;
|
||||
vertexOffset = 0;
|
||||
indexOffset = indexSize = 0;
|
||||
@ -463,7 +481,9 @@ void BlocksRenderer::build(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
std::shared_ptr<Mesh> BlocksRenderer::createMesh() {
|
||||
const vattr attrs[]{ {3}, {2}, {1}, {0} };
|
||||
size_t vcount = vertexOffset / BlocksRenderer::VERTEX_SIZE;
|
||||
return std::make_shared<Mesh>(vertexBuffer, vcount, indexBuffer, indexSize, attrs);
|
||||
return std::make_shared<Mesh>(
|
||||
vertexBuffer.get(), vcount, indexBuffer.get(), indexSize, attrs
|
||||
);
|
||||
}
|
||||
|
||||
std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const ChunksStorage* chunks) {
|
||||
@ -472,5 +492,5 @@ std::shared_ptr<Mesh> BlocksRenderer::render(const Chunk* chunk, const ChunksSto
|
||||
}
|
||||
|
||||
VoxelsVolume* BlocksRenderer::getVoxelsBuffer() const {
|
||||
return voxelsBuffer;
|
||||
return voxelsBuffer.get();
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
#include "../../voxels/voxel.hpp"
|
||||
#include "../../typedefs.hpp"
|
||||
@ -22,16 +23,15 @@ class BlocksRenderer {
|
||||
static const glm::vec3 SUN_VECTOR;
|
||||
static const uint VERTEX_SIZE;
|
||||
const Content* const content;
|
||||
float* vertexBuffer;
|
||||
int* indexBuffer;
|
||||
std::unique_ptr<float[]> vertexBuffer;
|
||||
std::unique_ptr<int[]> indexBuffer;
|
||||
size_t vertexOffset;
|
||||
size_t indexOffset, indexSize;
|
||||
size_t capacity;
|
||||
|
||||
int voxelBufferPadding = 2;
|
||||
bool overflow = false;
|
||||
|
||||
const Chunk* chunk = nullptr;
|
||||
VoxelsVolume* voxelsBuffer;
|
||||
std::unique_ptr<VoxelsVolume> voxelsBuffer;
|
||||
|
||||
const Block* const* blockDefsCache;
|
||||
const ContentGfxCache* const cache;
|
||||
@ -40,36 +40,41 @@ class BlocksRenderer {
|
||||
void vertex(const glm::vec3& coord, float u, float v, const glm::vec4& light);
|
||||
void index(int a, int b, int c, int d, int e, int f);
|
||||
|
||||
void vertex(const glm::vec3& coord, float u, float v,
|
||||
const glm::vec4& brightness,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const glm::vec3& axisZ);
|
||||
|
||||
void face(const glm::vec3& coord, float w, float h, float d,
|
||||
void vertex(
|
||||
const glm::vec3& coord, float u, float v,
|
||||
const glm::vec4& brightness,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const glm::vec3& axisZ
|
||||
);
|
||||
void face(
|
||||
const glm::vec3& coord,
|
||||
float w, float h, float d,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const glm::vec3& axisZ,
|
||||
const UVRegion& region,
|
||||
const glm::vec4(&lights)[4],
|
||||
const glm::vec4& tint);
|
||||
|
||||
void face(const glm::vec3& coord,
|
||||
const glm::vec4& tint
|
||||
);
|
||||
void face(
|
||||
const glm::vec3& coord,
|
||||
const glm::vec3& axisX,
|
||||
const glm::vec3& axisY,
|
||||
const glm::vec3& axisZ,
|
||||
const UVRegion& region,
|
||||
bool lights);
|
||||
|
||||
void tetragonicFace(const glm::vec3& coord,
|
||||
bool lights
|
||||
);
|
||||
void tetragonicFace(
|
||||
const glm::vec3& coord,
|
||||
const glm::vec3& p1, const glm::vec3& p2,
|
||||
const glm::vec3& p3, const glm::vec3& p4,
|
||||
const glm::vec3& X,
|
||||
const glm::vec3& Y,
|
||||
const glm::vec3& Z,
|
||||
const UVRegion& texreg,
|
||||
bool lights);
|
||||
|
||||
bool lights
|
||||
);
|
||||
void blockCube(
|
||||
int x, int y, int z,
|
||||
const UVRegion(&faces)[6],
|
||||
|
||||
@ -195,15 +195,16 @@ void WorldRenderer::renderLevel(
|
||||
}
|
||||
|
||||
void WorldRenderer::renderBlockSelection(Camera* camera, Shader* linesShader) {
|
||||
const auto& selection = player->selection;
|
||||
auto indices = level->content->getIndices();
|
||||
blockid_t id = PlayerController::selectedBlockId;
|
||||
blockid_t id = selection.vox.id;
|
||||
auto block = indices->getBlockDef(id);
|
||||
const glm::ivec3 pos = player->selectedBlockPosition;
|
||||
const glm::vec3 point = PlayerController::selectedPointPosition;
|
||||
const glm::vec3 norm = PlayerController::selectedBlockNormal;
|
||||
const glm::ivec3 pos = player->selection.position;
|
||||
const glm::vec3 point = selection.hitPosition;
|
||||
const glm::vec3 norm = selection.normal;
|
||||
|
||||
const std::vector<AABB>& hitboxes = block->rotatable
|
||||
? block->rt.hitboxes[PlayerController::selectedBlockRotation]
|
||||
? block->rt.hitboxes[selection.vox.state.rotation]
|
||||
: block->hitboxes;
|
||||
|
||||
linesShader->use();
|
||||
@ -305,7 +306,7 @@ void WorldRenderer::draw(
|
||||
ctx.setCullFace(true);
|
||||
renderLevel(ctx, camera, settings);
|
||||
// Selected block
|
||||
if (PlayerController::selectedBlockId != -1 && hudVisible){
|
||||
if (player->selection.vox.id != BLOCK_VOID && hudVisible){
|
||||
renderBlockSelection(camera, linesShader);
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,11 +130,13 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
for (uint z = padding; z < d-padding; z++){
|
||||
for (uint x = padding; x < w-padding; x++){
|
||||
int index = z * w + x;
|
||||
if ((index + tickid) % parts != 0)
|
||||
if ((index + tickid) % parts != 0) {
|
||||
continue;
|
||||
}
|
||||
auto& chunk = chunks->chunks[index];
|
||||
if (chunk == nullptr || !chunk->flags.lighted)
|
||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||
continue;
|
||||
}
|
||||
for (int s = 0; s < segments; s++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int bx = random.rand() % CHUNK_W;
|
||||
@ -146,7 +148,8 @@ void BlocksController::randomTick(int tickid, int parts) {
|
||||
scripting::random_update_block(
|
||||
block,
|
||||
chunk->x * CHUNK_W + bx, by,
|
||||
chunk->z * CHUNK_D + bz);
|
||||
chunk->z * CHUNK_D + bz
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,11 +170,6 @@ void CameraControl::update(const PlayerInput& input, float delta, Chunks* chunks
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 PlayerController::selectedPointPosition;
|
||||
glm::ivec3 PlayerController::selectedBlockNormal;
|
||||
int PlayerController::selectedBlockId = -1;
|
||||
int PlayerController::selectedBlockRotation = 0;
|
||||
|
||||
PlayerController::PlayerController(
|
||||
Level* level,
|
||||
const EngineSettings& settings,
|
||||
@ -252,8 +247,8 @@ void PlayerController::update(float delta, bool input, bool pause) {
|
||||
if (input) {
|
||||
updateInteraction();
|
||||
} else {
|
||||
selectedBlockId = -1;
|
||||
selectedBlockRotation = 0;
|
||||
player->selection.vox.id = BLOCK_VOID;
|
||||
player->selection.vox.state.rotation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,7 +290,7 @@ void PlayerController::updateControls(float delta){
|
||||
player->updateInput(level, input, delta);
|
||||
}
|
||||
|
||||
static int determine_rotation(Block* def, glm::ivec3& norm, glm::vec3& camDir) {
|
||||
static int determine_rotation(Block* def, const glm::ivec3& norm, glm::vec3& camDir) {
|
||||
if (def && def->rotatable){
|
||||
const std::string& name = def->rotations.name;
|
||||
if (name == "pipe") {
|
||||
@ -336,25 +331,11 @@ static void pick_block(ContentIndices* indices, Chunks* chunks, Player* player,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor this nesting nest
|
||||
void PlayerController::updateInteraction(){
|
||||
voxel* PlayerController::updateSelection(float maxDistance) {
|
||||
auto indices = level->content->getIndices();
|
||||
Chunks* chunks = level->chunks.get();
|
||||
Lighting* lighting = level->lighting.get();
|
||||
Camera* camera = player->camera.get();
|
||||
|
||||
bool xkey = Events::pressed(keycode::X);
|
||||
bool lclick = Events::jactive(BIND_PLAYER_ATTACK) ||
|
||||
(xkey && Events::active(BIND_PLAYER_ATTACK));
|
||||
bool rclick = Events::jactive(BIND_PLAYER_BUILD) ||
|
||||
(xkey && Events::active(BIND_PLAYER_BUILD));
|
||||
float maxDistance = 10.0f;
|
||||
if (xkey) {
|
||||
maxDistance *= 20.0f;
|
||||
}
|
||||
auto inventory = player->getInventory();
|
||||
const ItemStack& stack = inventory->getSlot(player->getChosenSlot());
|
||||
ItemDef* item = indices->getItemDef(stack.getItemId());
|
||||
auto chunks = level->chunks.get();
|
||||
auto camera = player->camera.get();
|
||||
auto& selection = player->selection;
|
||||
|
||||
glm::vec3 end;
|
||||
glm::ivec3 iend;
|
||||
@ -365,95 +346,134 @@ void PlayerController::updateInteraction(){
|
||||
maxDistance,
|
||||
end, norm, iend
|
||||
);
|
||||
if (vox != nullptr) {
|
||||
player->selectedVoxel = *vox;
|
||||
selectedBlockId = vox->id;
|
||||
selectedBlockRotation = vox->state.rotation;
|
||||
player->selectedBlockPosition = iend;
|
||||
selectedPointPosition = end;
|
||||
selectedBlockNormal = norm;
|
||||
int x = iend.x;
|
||||
int y = iend.y;
|
||||
int z = iend.z;
|
||||
|
||||
Block* def = indices->getBlockDef(item->rt.placingBlock);
|
||||
blockstate state {};
|
||||
state.rotation = determine_rotation(def, norm, camera->dir);
|
||||
|
||||
if (lclick && !input.shift && item->rt.funcsset.on_block_break_by) {
|
||||
if (scripting::on_item_break_block(player.get(), item, x, y, z))
|
||||
return;
|
||||
}
|
||||
|
||||
Block* target = indices->getBlockDef(vox->id);
|
||||
if (lclick && target->breakable){
|
||||
onBlockInteraction(
|
||||
glm::ivec3(x, y, z), target,
|
||||
BlockInteraction::destruction
|
||||
);
|
||||
blocksController->breakBlock(player.get(), target, x, y, z);
|
||||
}
|
||||
if (rclick && !input.shift) {
|
||||
bool preventDefault = false;
|
||||
if (item->rt.funcsset.on_use_on_block) {
|
||||
preventDefault = scripting::on_item_use_on_block(player.get(), item, x, y, z);
|
||||
} else if (item->rt.funcsset.on_use) {
|
||||
preventDefault = scripting::on_item_use(player.get(), item);
|
||||
}
|
||||
if (preventDefault) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (def && rclick){
|
||||
if (!input.shift && target->rt.funcsset.oninteract) {
|
||||
if (scripting::on_block_interact(player.get(), target, x, y, z))
|
||||
return;
|
||||
}
|
||||
if (!target->replaceable){
|
||||
x = (iend.x)+(norm.x);
|
||||
y = (iend.y)+(norm.y);
|
||||
z = (iend.z)+(norm.z);
|
||||
} else {
|
||||
if (def->rotations.name == "pipe") {
|
||||
state.rotation = BLOCK_DIR_UP;
|
||||
}
|
||||
}
|
||||
vox = chunks->get(x, y, z);
|
||||
blockid_t chosenBlock = def->rt.id;
|
||||
if (vox && (target = indices->getBlockDef(vox->id))->replaceable) {
|
||||
if (!level->physics->isBlockInside(x,y,z,def,state, player->hitbox.get())
|
||||
|| !def->obstacle){
|
||||
if (def->grounded && !chunks->isSolidBlock(x, y-1, z)) {
|
||||
chosenBlock = 0;
|
||||
}
|
||||
if (chosenBlock != vox->id && chosenBlock) {
|
||||
onBlockInteraction(
|
||||
glm::ivec3(x, y, z), def,
|
||||
BlockInteraction::placing
|
||||
);
|
||||
chunks->set(x, y, z, chosenBlock, state);
|
||||
lighting->onBlockSet(x,y,z, chosenBlock);
|
||||
if (def->rt.funcsset.onplaced) {
|
||||
scripting::on_block_placed(player.get(), def, x, y, z);
|
||||
}
|
||||
blocksController->updateSides(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Events::jactive(BIND_PLAYER_PICK)) {
|
||||
pick_block(indices, chunks, player.get(), x, y, z);
|
||||
if (vox == nullptr) {
|
||||
selection.vox = {BLOCK_VOID, {}};
|
||||
return nullptr;
|
||||
}
|
||||
blockstate selectedState = vox->state;
|
||||
selection.vox = *vox;
|
||||
selection.actualPosition = iend;
|
||||
if (selectedState.segment) {
|
||||
selection.position = chunks->seekOrigin(
|
||||
iend, indices->getBlockDef(selection.vox.id), selectedState
|
||||
);
|
||||
auto origin = chunks->get(iend);
|
||||
if (origin && origin->id != vox->id) {
|
||||
chunks->set(iend.x, iend.y, iend.z, 0, {});
|
||||
return updateSelection(maxDistance);
|
||||
}
|
||||
} else {
|
||||
selectedBlockId = -1;
|
||||
selectedBlockRotation = 0;
|
||||
player->selectedVoxel.id = BLOCK_VOID;
|
||||
if (rclick) {
|
||||
if (item->rt.funcsset.on_use) {
|
||||
scripting::on_item_use(player.get(), item);
|
||||
}
|
||||
selection.position = iend;
|
||||
}
|
||||
selection.hitPosition = end;
|
||||
selection.normal = norm;
|
||||
return vox;
|
||||
}
|
||||
|
||||
void PlayerController::processRightClick(Block* def, Block* target) {
|
||||
const auto& selection = player->selection;
|
||||
auto chunks = level->chunks.get();
|
||||
auto camera = player->camera.get();
|
||||
auto lighting = level->lighting.get();
|
||||
|
||||
blockstate state {};
|
||||
state.rotation = determine_rotation(def, selection.normal, camera->dir);
|
||||
|
||||
if (!input.shift && target->rt.funcsset.oninteract) {
|
||||
if (scripting::on_block_interact(player.get(), target, selection.position)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto coord = selection.actualPosition;
|
||||
if (!target->replaceable){
|
||||
coord += selection.normal;
|
||||
} else if (def->rotations.name == BlockRotProfile::PIPE_NAME) {
|
||||
state.rotation = BLOCK_DIR_UP;
|
||||
}
|
||||
blockid_t chosenBlock = def->rt.id;
|
||||
|
||||
if (def->obstacle && level->physics->isBlockInside(
|
||||
coord.x, coord.y, coord.z, def,state, player->hitbox.get())) {
|
||||
return;
|
||||
}
|
||||
auto vox = chunks->get(coord);
|
||||
if (vox == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!chunks->checkReplaceability(def, state, coord)) {
|
||||
return;
|
||||
}
|
||||
if (def->grounded && !chunks->isSolidBlock(coord.x, coord.y-1, coord.z)) {
|
||||
return;
|
||||
}
|
||||
if (chosenBlock != vox->id && chosenBlock) {
|
||||
onBlockInteraction(coord, def, BlockInteraction::placing);
|
||||
chunks->set(coord.x, coord.y, coord.z, chosenBlock, state);
|
||||
lighting->onBlockSet(coord.x, coord.y, coord.z, chosenBlock);
|
||||
if (def->rt.funcsset.onplaced) {
|
||||
scripting::on_block_placed(player.get(), def, coord.x, coord.y, coord.z);
|
||||
}
|
||||
blocksController->updateSides(coord.x, coord.y, coord.z);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerController::updateInteraction() {
|
||||
auto indices = level->content->getIndices();
|
||||
auto chunks = level->chunks.get();
|
||||
const auto& selection = player->selection;
|
||||
|
||||
bool xkey = Events::pressed(keycode::X);
|
||||
bool lclick = Events::jactive(BIND_PLAYER_ATTACK) || (xkey && Events::active(BIND_PLAYER_ATTACK));
|
||||
bool rclick = Events::jactive(BIND_PLAYER_BUILD) || (xkey && Events::active(BIND_PLAYER_BUILD));
|
||||
float maxDistance = xkey ? 200.0f : 10.0f;
|
||||
|
||||
auto inventory = player->getInventory();
|
||||
const ItemStack& stack = inventory->getSlot(player->getChosenSlot());
|
||||
ItemDef* item = indices->getItemDef(stack.getItemId());
|
||||
|
||||
auto vox = updateSelection(maxDistance);
|
||||
if (vox == nullptr) {
|
||||
if (rclick && item->rt.funcsset.on_use) {
|
||||
scripting::on_item_use(player.get(), item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto iend = selection.position;
|
||||
if (lclick && !input.shift && item->rt.funcsset.on_block_break_by) {
|
||||
if (scripting::on_item_break_block(player.get(), item, iend.x, iend.y, iend.z)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto target = indices->getBlockDef(vox->id);
|
||||
if (lclick && target->breakable){
|
||||
onBlockInteraction(
|
||||
iend, target,
|
||||
BlockInteraction::destruction
|
||||
);
|
||||
blocksController->breakBlock(player.get(), target, iend.x, iend.y, iend.z);
|
||||
}
|
||||
if (rclick && !input.shift) {
|
||||
bool preventDefault = false;
|
||||
if (item->rt.funcsset.on_use_on_block) {
|
||||
preventDefault = scripting::on_item_use_on_block(
|
||||
player.get(), item, iend.x, iend.y, iend.z
|
||||
);
|
||||
} else if (item->rt.funcsset.on_use) {
|
||||
preventDefault = scripting::on_item_use(player.get(), item);
|
||||
}
|
||||
if (preventDefault) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto def = indices->getBlockDef(item->rt.placingBlock);
|
||||
if (def && rclick) {
|
||||
processRightClick(def, target);
|
||||
}
|
||||
if (Events::jactive(BIND_PLAYER_PICK)) {
|
||||
auto coord = selection.actualPosition;
|
||||
pick_block(indices, chunks, player.get(), coord.x, coord.y, coord.z);
|
||||
}
|
||||
}
|
||||
|
||||
Player* PlayerController::getPlayer() {
|
||||
|
||||
@ -76,12 +76,10 @@ class PlayerController {
|
||||
float stepsTimer = 0.0f;
|
||||
void onFootstep();
|
||||
void updateFootsteps(float delta);
|
||||
public:
|
||||
static glm::ivec3 selectedBlockNormal;
|
||||
static glm::vec3 selectedPointPosition;
|
||||
static int selectedBlockId;
|
||||
static int selectedBlockRotation;
|
||||
void processRightClick(Block* def, Block* target);
|
||||
|
||||
voxel* updateSelection(float maxDistance);
|
||||
public:
|
||||
PlayerController(
|
||||
Level* level,
|
||||
const EngineSettings& settings,
|
||||
|
||||
@ -13,29 +13,32 @@
|
||||
#include "../../../content/Content.hpp"
|
||||
#include "../../../logic/BlocksController.hpp"
|
||||
|
||||
int l_block_name(lua_State* L) {
|
||||
static Block* require_block(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua_Integer id = lua_tointeger(L, 1);
|
||||
if (static_cast<size_t>(id) >= indices->countBlockDefs()) {
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
auto def = indices->getBlockDef(id);
|
||||
lua_pushstring(L, def->name.c_str());
|
||||
return 1;
|
||||
return indices->getBlockDef(id);
|
||||
}
|
||||
|
||||
int l_block_material(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua_Integer id = lua_tointeger(L, 1);
|
||||
if (static_cast<size_t>(id) >= indices->countBlockDefs()) {
|
||||
return 0;
|
||||
static int l_name(lua_State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
lua_pushstring(L, def->name.c_str());
|
||||
return 1;
|
||||
}
|
||||
auto def = indices->getBlockDef(id);
|
||||
lua_pushstring(L, def->material.c_str());
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l_is_solid_at(lua_State* L) {
|
||||
static int l_material(lua_State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
lua_pushstring(L, def->material.c_str());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_solid_at(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -44,18 +47,53 @@ int l_is_solid_at(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_blocks_count(lua_State* L) {
|
||||
static int l_count(lua_State* L) {
|
||||
lua_pushinteger(L, scripting::indices->countBlockDefs());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_block_index(lua_State* L) {
|
||||
static int l_index(lua_State* L) {
|
||||
std::string name = lua_tostring(L, 1);
|
||||
lua_pushinteger(L, scripting::content->requireBlock(name).rt.id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_set_block(lua_State* L) {
|
||||
static int l_is_extended(lua_State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
lua_pushboolean(L, def->rt.extended);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_size(lua_State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
return lua::pushivec3(L, def->size.x, def->size.y, def->size.z);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_segment(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
auto vox = scripting::level->chunks->get(x, y, z);
|
||||
|
||||
lua_pushboolean(L, vox->state.segment);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_seek_origin(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
auto vox = scripting::level->chunks->get(x, y, z);
|
||||
auto def = scripting::indices->getBlockDef(vox->id);
|
||||
|
||||
return lua::pushivec3(L, scripting::level->chunks->seekOrigin({x, y, z}, def, vox->state));
|
||||
}
|
||||
|
||||
static int l_set(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -76,7 +114,7 @@ int l_set_block(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l_get_block(lua_State* L) {
|
||||
static int l_get(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -86,7 +124,7 @@ int l_get_block(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_get_block_x(lua_State* L) {
|
||||
static int l_get_x(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -103,7 +141,7 @@ int l_get_block_x(lua_State* L) {
|
||||
}
|
||||
}
|
||||
|
||||
int l_get_block_y(lua_State* L) {
|
||||
static int l_get_y(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -120,7 +158,7 @@ int l_get_block_y(lua_State* L) {
|
||||
}
|
||||
}
|
||||
|
||||
int l_get_block_z(lua_State* L) {
|
||||
static int l_get_z(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -137,7 +175,7 @@ int l_get_block_z(lua_State* L) {
|
||||
}
|
||||
}
|
||||
|
||||
int l_get_block_rotation(lua_State* L) {
|
||||
static int l_get_rotation(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -147,21 +185,16 @@ int l_get_block_rotation(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_set_block_rotation(lua_State* L) {
|
||||
static int l_set_rotation(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
lua_Integer value = lua_tointeger(L, 4);
|
||||
voxel* vox = scripting::level->chunks->get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
vox->state.rotation = value;
|
||||
scripting::level->chunks->getChunkByVoxel(x, y, z)->setModifiedAndUnsaved();
|
||||
scripting::level->chunks->setRotation(x, y, z, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l_get_block_states(lua_State* L) {
|
||||
static int l_get_states(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -171,7 +204,7 @@ int l_get_block_states(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_set_block_states(lua_State* L) {
|
||||
static int l_set_states(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -187,7 +220,7 @@ int l_set_block_states(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l_get_block_user_bits(lua_State* L) {
|
||||
static int l_get_user_bits(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -205,7 +238,7 @@ int l_get_block_user_bits(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_set_block_user_bits(lua_State* L) {
|
||||
static int l_set_user_bits(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -228,7 +261,7 @@ int l_set_block_user_bits(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l_is_replaceable_at(lua_State* L) {
|
||||
static int l_is_replaceable_at(lua_State* L) {
|
||||
lua_Integer x = lua_tointeger(L, 1);
|
||||
lua_Integer y = lua_tointeger(L, 2);
|
||||
lua_Integer z = lua_tointeger(L, 3);
|
||||
@ -237,35 +270,36 @@ int l_is_replaceable_at(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int l_block_caption(lua_State* L) {
|
||||
auto indices = scripting::content->getIndices();
|
||||
lua_Integer id = lua_tointeger(L, 1);
|
||||
if (static_cast<size_t>(id) >= indices->countBlockDefs()) {
|
||||
return 0;
|
||||
static int l_caption(lua_State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
lua_pushstring(L, def->caption.c_str());
|
||||
return 1;
|
||||
}
|
||||
auto def = indices->getBlockDef(id);
|
||||
lua_pushstring(L, def->caption.c_str());
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg blocklib [] = {
|
||||
{"index", lua_wrap_errors<l_block_index>},
|
||||
{"name", lua_wrap_errors<l_block_name>},
|
||||
{"material", lua_wrap_errors<l_block_material>},
|
||||
{"caption", lua_wrap_errors<l_block_caption>},
|
||||
{"defs_count", lua_wrap_errors<l_blocks_count>},
|
||||
{"index", lua_wrap_errors<l_index>},
|
||||
{"name", lua_wrap_errors<l_name>},
|
||||
{"material", lua_wrap_errors<l_material>},
|
||||
{"caption", lua_wrap_errors<l_caption>},
|
||||
{"defs_count", lua_wrap_errors<l_count>},
|
||||
{"is_solid_at", lua_wrap_errors<l_is_solid_at>},
|
||||
{"is_replaceable_at", lua_wrap_errors<l_is_replaceable_at>},
|
||||
{"set", lua_wrap_errors<l_set_block>},
|
||||
{"get", lua_wrap_errors<l_get_block>},
|
||||
{"get_X", lua_wrap_errors<l_get_block_x>},
|
||||
{"get_Y", lua_wrap_errors<l_get_block_y>},
|
||||
{"get_Z", lua_wrap_errors<l_get_block_z>},
|
||||
{"get_states", lua_wrap_errors<l_get_block_states>},
|
||||
{"set_states", lua_wrap_errors<l_set_block_states>},
|
||||
{"get_rotation", lua_wrap_errors<l_get_block_rotation>},
|
||||
{"set_rotation", lua_wrap_errors<l_set_block_rotation>},
|
||||
{"get_user_bits", lua_wrap_errors<l_get_block_user_bits>},
|
||||
{"set_user_bits", lua_wrap_errors<l_set_block_user_bits>},
|
||||
{"set", lua_wrap_errors<l_set>},
|
||||
{"get", lua_wrap_errors<l_get>},
|
||||
{"get_X", lua_wrap_errors<l_get_x>},
|
||||
{"get_Y", lua_wrap_errors<l_get_y>},
|
||||
{"get_Z", lua_wrap_errors<l_get_z>},
|
||||
{"get_states", lua_wrap_errors<l_get_states>},
|
||||
{"set_states", lua_wrap_errors<l_set_states>},
|
||||
{"get_rotation", lua_wrap_errors<l_get_rotation>},
|
||||
{"set_rotation", lua_wrap_errors<l_set_rotation>},
|
||||
{"get_user_bits", lua_wrap_errors<l_get_user_bits>},
|
||||
{"set_user_bits", lua_wrap_errors<l_set_user_bits>},
|
||||
{"is_extended", lua_wrap_errors<l_is_extended>},
|
||||
{"get_size", lua_wrap_errors<l_get_size>},
|
||||
{"is_segment", lua_wrap_errors<l_is_segment>},
|
||||
{"seek_origin", lua_wrap_errors<l_seek_origin>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
@ -134,10 +134,10 @@ static int l_player_set_noclip(lua_State* L) {
|
||||
|
||||
static int l_player_get_selected_block(lua_State* L) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
if (player->selectedVoxel.id == BLOCK_VOID) {
|
||||
if (player->selection.vox.id == BLOCK_VOID) {
|
||||
return 0;
|
||||
}
|
||||
const glm::ivec3 pos = player->selectedBlockPosition;
|
||||
const glm::ivec3 pos = player->selection.position;
|
||||
lua_pushinteger(L, pos.x);
|
||||
lua_pushinteger(L, pos.y);
|
||||
lua_pushinteger(L, pos.z);
|
||||
|
||||
@ -21,6 +21,13 @@ namespace lua {
|
||||
return 3;
|
||||
}
|
||||
|
||||
inline int pushivec3(lua_State* L, glm::ivec3 vec) {
|
||||
lua_pushinteger(L, vec.x);
|
||||
lua_pushinteger(L, vec.y);
|
||||
lua_pushinteger(L, vec.z);
|
||||
return 3;
|
||||
}
|
||||
|
||||
inline int pushvec3(lua_State* L, glm::vec3 vec) {
|
||||
lua_pushnumber(L, vec.x);
|
||||
lua_pushnumber(L, vec.y);
|
||||
|
||||
@ -192,10 +192,10 @@ void scripting::on_block_broken(Player* player, const Block* block, int x, int y
|
||||
});
|
||||
}
|
||||
|
||||
bool scripting::on_block_interact(Player* player, const Block* block, int x, int y, int z) {
|
||||
bool scripting::on_block_interact(Player* player, const Block* block, glm::ivec3 pos) {
|
||||
std::string name = block->name + ".interact";
|
||||
return state->emit_event(name, [x, y, z, player] (lua::LuaState* state) {
|
||||
state->pushivec3(x, y, z);
|
||||
return state->emit_event(name, [pos, player] (lua::LuaState* state) {
|
||||
state->pushivec3(pos.x, pos.y, pos.z);
|
||||
state->pushinteger(player->getId());
|
||||
return 4;
|
||||
});
|
||||
|
||||
@ -58,7 +58,7 @@ namespace scripting {
|
||||
void random_update_block(const Block* block, int x, int y, int z);
|
||||
void on_block_placed(Player* player, const Block* block, int x, int y, int z);
|
||||
void on_block_broken(Player* player, const Block* block, int x, int y, int z);
|
||||
bool on_block_interact(Player* player, const Block* block, int x, int y, int z);
|
||||
bool on_block_interact(Player* player, const Block* block, glm::ivec3 pos);
|
||||
|
||||
/// @brief Called on RMB click with the item selected
|
||||
/// @return true if prevents default action
|
||||
|
||||
@ -3,22 +3,27 @@
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
// Axis Aligned Bounding Box
|
||||
/// @brief Axis Aligned Bounding Box
|
||||
struct AABB {
|
||||
glm::vec3 a {0.0f};
|
||||
glm::vec3 b {1.0f};
|
||||
|
||||
/* Get AABB point with minimal x,y,z */
|
||||
AABB() {}
|
||||
|
||||
AABB(glm::vec3 size) : a(0.0f), b(size) {
|
||||
}
|
||||
|
||||
/// @brief Get AABB point with minimal x,y,z
|
||||
inline glm::vec3 min() const {
|
||||
return glm::min(a, b);
|
||||
}
|
||||
|
||||
/* Get AABB point with minimal x,y,z */
|
||||
/// @brief Get AABB point with minimal x,y,z
|
||||
inline glm::vec3 max() const {
|
||||
return glm::max(a, b);
|
||||
}
|
||||
|
||||
/* Get AABB dimensions: width, height, depth */
|
||||
/// @brief Get AABB dimensions: width, height, depth
|
||||
inline glm::vec3 size() const {
|
||||
return glm::vec3(
|
||||
fabs(b.x - a.x),
|
||||
@ -31,14 +36,14 @@ struct AABB {
|
||||
return (a + b) * 0.5f;
|
||||
}
|
||||
|
||||
/* Multiply AABB size from center */
|
||||
/// @brief Multiply AABB size from center
|
||||
inline void scale(const glm::vec3 mul) {
|
||||
glm::vec3 center = (a + b) * 0.5f;
|
||||
a = (a - center) * mul + center;
|
||||
b = (b - center) * mul + center;
|
||||
}
|
||||
|
||||
/* Multiply AABB size from given origin */
|
||||
/// @brief Multiply AABB size from given origin
|
||||
inline void scale(const glm::vec3 mul, const glm::vec3 orig) {
|
||||
glm::vec3 beg = min();
|
||||
glm::vec3 end = max();
|
||||
@ -47,7 +52,7 @@ struct AABB {
|
||||
b = (b - center) * mul + center;
|
||||
}
|
||||
|
||||
/* Check if given point is inside */
|
||||
/// @brief Check if given point is inside
|
||||
inline bool contains(const glm::vec3 pos) const {
|
||||
const glm::vec3 p = min();
|
||||
const glm::vec3 s = size();
|
||||
|
||||
@ -32,6 +32,14 @@ struct PlayerInput {
|
||||
bool flight : 1;
|
||||
};
|
||||
|
||||
struct BlockSelection {
|
||||
voxel vox {0, {}};
|
||||
glm::ivec3 position {};
|
||||
glm::ivec3 actualPosition {};
|
||||
glm::ivec3 normal {};
|
||||
glm::vec3 hitPosition;
|
||||
};
|
||||
|
||||
class Player : public Object, public Serializable {
|
||||
float speed;
|
||||
int chosenSlot;
|
||||
@ -44,9 +52,8 @@ public:
|
||||
std::shared_ptr<Camera> currentCamera;
|
||||
std::unique_ptr<Hitbox> hitbox;
|
||||
bool debug = false;
|
||||
voxel selectedVoxel {0, {}};
|
||||
glm::vec3 cam {};
|
||||
glm::ivec3 selectedBlockPosition {};
|
||||
BlockSelection selection {};
|
||||
|
||||
Player(glm::vec3 position, float speed, std::shared_ptr<Inventory> inv);
|
||||
~Player();
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
#include "../maths/UVRegion.hpp"
|
||||
#include "../typedefs.hpp"
|
||||
|
||||
#define BLOCK_ITEM_SUFFIX ".item"
|
||||
inline std::string BLOCK_ITEM_SUFFIX = ".item";
|
||||
|
||||
inline constexpr uint FACE_MX = 0;
|
||||
inline constexpr uint FACE_PX = 1;
|
||||
@ -62,6 +62,9 @@ struct BlockRotProfile {
|
||||
|
||||
/// @brief Doors, signs and other panes
|
||||
static const BlockRotProfile PANE;
|
||||
|
||||
static inline std::string PIPE_NAME = "pipe";
|
||||
static inline std::string PANE_NAME = "pane";
|
||||
};
|
||||
|
||||
enum class BlockModel {
|
||||
@ -110,6 +113,8 @@ public:
|
||||
/// @brief Light emission R, G, B, S (sky lights: sun, moon, radioactive clouds)
|
||||
uint8_t emission[4] {0, 0, 0, 0};
|
||||
|
||||
glm::i8vec3 size {1, 1, 1};
|
||||
|
||||
/// @brief Influences visible block sides for transparent blocks
|
||||
uint8_t drawGroup = 0;
|
||||
|
||||
@ -175,6 +180,9 @@ public:
|
||||
|
||||
/// @brief does the block emit any lights
|
||||
bool emissive = false;
|
||||
|
||||
// @brief block size is greather than 1x1x1
|
||||
bool extended = false;
|
||||
|
||||
/// @brief set of hitboxes sets with all coord-systems precalculated
|
||||
std::vector<AABB> hitboxes[BlockRotProfile::MAX_COUNT];
|
||||
@ -191,4 +199,4 @@ public:
|
||||
Block(const Block&) = delete;
|
||||
};
|
||||
|
||||
#endif /* VOXELS_BLOCK_HPP_ */
|
||||
#endif // VOXELS_BLOCK_HPP_
|
||||
|
||||
@ -22,7 +22,7 @@ Chunks::Chunks(
|
||||
WorldFiles* wfile,
|
||||
LevelEvents* events,
|
||||
const Content* content
|
||||
) : contentIds(content->getIndices()),
|
||||
) : indices(content->getIndices()),
|
||||
chunks(w*d),
|
||||
chunksSecond(w*d),
|
||||
w(w), d(d), ox(ox), oz(oz),
|
||||
@ -33,7 +33,7 @@ Chunks::Chunks(
|
||||
chunksCount = 0;
|
||||
}
|
||||
|
||||
voxel* Chunks::get(int32_t x, int32_t y, int32_t z) {
|
||||
voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const {
|
||||
x -= ox * CHUNK_W;
|
||||
z -= oz * CHUNK_D;
|
||||
int cx = floordiv(x, CHUNK_W);
|
||||
@ -65,13 +65,18 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z){
|
||||
return ∅
|
||||
}
|
||||
}
|
||||
const Block* def = contentIds->getBlockDef(v->id);
|
||||
const Block* def = indices->getBlockDef(v->id);
|
||||
if (def->obstacle) {
|
||||
glm::ivec3 offset {};
|
||||
if (v->state.segment) {
|
||||
glm::ivec3 point(ix, iy, iz);
|
||||
offset = seekOrigin(point, def, v->state) - point;
|
||||
}
|
||||
const auto& boxes = def->rotatable
|
||||
? def->rt.hitboxes[v->state.rotation]
|
||||
: def->hitboxes;
|
||||
for (const auto& hitbox : boxes) {
|
||||
if (hitbox.contains({x - ix, y - iy, z - iz})) {
|
||||
if (hitbox.contains({x - ix - offset.x, y - iy - offset.y, z - iz - offset.z})) {
|
||||
return &hitbox;
|
||||
}
|
||||
}
|
||||
@ -83,21 +88,21 @@ bool Chunks::isSolidBlock(int32_t x, int32_t y, int32_t z) {
|
||||
voxel* v = get(x, y, z);
|
||||
if (v == nullptr)
|
||||
return false;
|
||||
return contentIds->getBlockDef(v->id)->rt.solid;
|
||||
return indices->getBlockDef(v->id)->rt.solid;
|
||||
}
|
||||
|
||||
bool Chunks::isReplaceableBlock(int32_t x, int32_t y, int32_t z) {
|
||||
voxel* v = get(x, y, z);
|
||||
if (v == nullptr)
|
||||
return false;
|
||||
return contentIds->getBlockDef(v->id)->replaceable;
|
||||
return indices->getBlockDef(v->id)->replaceable;
|
||||
}
|
||||
|
||||
bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) {
|
||||
voxel* v = get(x, y, z);
|
||||
if (v == nullptr)
|
||||
return false;
|
||||
return contentIds->getBlockDef(v->id)->obstacle;
|
||||
return indices->getBlockDef(v->id)->obstacle;
|
||||
}
|
||||
|
||||
ubyte Chunks::getLight(int32_t x, int32_t y, int32_t z, int channel){
|
||||
@ -153,33 +158,217 @@ Chunk* Chunks::getChunkByVoxel(int32_t x, int32_t y, int32_t z) {
|
||||
Chunk* Chunks::getChunk(int x, int z){
|
||||
x -= ox;
|
||||
z -= oz;
|
||||
if (x < 0 || z < 0 || x >= int(w) || z >= int(d))
|
||||
if (x < 0 || z < 0 || x >= static_cast<int>(w) || z >= static_cast<int>(d)) {
|
||||
return nullptr;
|
||||
}
|
||||
return chunks[z * w + x].get();
|
||||
}
|
||||
|
||||
void Chunks::set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state) {
|
||||
if (y < 0 || y >= CHUNK_H)
|
||||
glm::ivec3 Chunks::seekOrigin(glm::ivec3 pos, const Block* def, blockstate state) {
|
||||
const auto& rotation = def->rotations.variants[state.rotation];
|
||||
auto segment = state.segment;
|
||||
while (true) {
|
||||
if (!segment) {
|
||||
return pos;
|
||||
}
|
||||
if (segment & 1) pos -= rotation.axisX;
|
||||
if (segment & 2) pos -= rotation.axisY;
|
||||
if (segment & 4) pos -= rotation.axisZ;
|
||||
|
||||
if (auto* voxel = get(pos.x, pos.y, pos.z)) {
|
||||
segment = voxel->state.segment;
|
||||
} else {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chunks::eraseSegments(const Block* def, blockstate state, int x, int y, int z) {
|
||||
const auto& rotation = def->rotations.variants[state.rotation];
|
||||
for (int sy = 0; sy < def->size.y; sy++) {
|
||||
for (int sz = 0; sz < def->size.z; sz++) {
|
||||
for (int sx = 0; sx < def->size.x; sx++) {
|
||||
if ((sx | sy | sz) == 0) {
|
||||
continue;
|
||||
}
|
||||
glm::ivec3 pos(x, y, z);
|
||||
pos += rotation.axisX * sx;
|
||||
pos += rotation.axisY * sy;
|
||||
pos += rotation.axisZ * sz;
|
||||
set(pos.x, pos.y, pos.z, 0, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint8_t segment_to_int(int sx, int sy, int sz) {
|
||||
return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2));
|
||||
}
|
||||
|
||||
void Chunks::repairSegments(const Block* def, blockstate state, int x, int y, int z) {
|
||||
const auto& rotation = def->rotations.variants[state.rotation];
|
||||
const auto id = def->rt.id;
|
||||
const auto size = def->size;
|
||||
for (int sy = 0; sy < size.y; sy++) {
|
||||
for (int sz = 0; sz < size.z; sz++) {
|
||||
for (int sx = 0; sx < size.x; sx++) {
|
||||
if ((sx | sy | sz) == 0) {
|
||||
continue;
|
||||
}
|
||||
blockstate segState = state;
|
||||
segState.segment = segment_to_int(sx, sy, sz);
|
||||
|
||||
glm::ivec3 pos(x, y, z);
|
||||
pos += rotation.axisX * sx;
|
||||
pos += rotation.axisY * sy;
|
||||
pos += rotation.axisZ * sz;
|
||||
set(pos.x, pos.y, pos.z, id, segState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Chunks::checkReplaceability(const Block* def, blockstate state, glm::ivec3 origin, blockid_t ignore) {
|
||||
const auto& rotation = def->rotations.variants[state.rotation];
|
||||
const auto size = def->size;
|
||||
for (int sy = 0; sy < size.y; sy++) {
|
||||
for (int sz = 0; sz < size.z; sz++) {
|
||||
for (int sx = 0; sx < size.x; sx++) {
|
||||
blockstate segState = state;
|
||||
segState.segment = segment_to_int(sx, sy, sz);
|
||||
|
||||
auto pos = origin;
|
||||
pos += rotation.axisX * sx;
|
||||
pos += rotation.axisY * sy;
|
||||
pos += rotation.axisZ * sz;
|
||||
if (auto vox = get(pos.x, pos.y, pos.z)) {
|
||||
auto target = indices->getBlockDef(vox->id);
|
||||
if (!target->replaceable && vox->id != ignore) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Chunks::setRotationExtended(
|
||||
Block* def, blockstate state, glm::ivec3 origin, uint8_t index
|
||||
) {
|
||||
auto newstate = state;
|
||||
newstate.rotation = index;
|
||||
|
||||
// unable to rotate block (cause: obstacles)
|
||||
if (!checkReplaceability(def, newstate, origin, def->rt.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& rotation = def->rotations.variants[index];
|
||||
const auto size = def->size;
|
||||
std::vector<glm::ivec3> segmentBlocks;
|
||||
|
||||
for (int sy = 0; sy < size.y; sy++) {
|
||||
for (int sz = 0; sz < size.z; sz++) {
|
||||
for (int sx = 0; sx < size.x; sx++) {
|
||||
auto pos = origin;
|
||||
pos += rotation.axisX * sx;
|
||||
pos += rotation.axisY * sy;
|
||||
pos += rotation.axisZ * sz;
|
||||
|
||||
blockstate segState = newstate;
|
||||
segState.segment = segment_to_int(sx, sy, sz);
|
||||
|
||||
auto vox = get(pos);
|
||||
// checked for nullptr by checkReplaceability
|
||||
if (vox->id != def->rt.id) {
|
||||
set(pos.x, pos.y, pos.z, def->rt.id, segState);
|
||||
} else {
|
||||
vox->state = segState;
|
||||
auto chunk = getChunkByVoxel(pos.x, pos.y, pos.z);
|
||||
chunk->setModifiedAndUnsaved();
|
||||
segmentBlocks.emplace_back(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto& prevRotation = def->rotations.variants[state.rotation];
|
||||
for (int sy = 0; sy < size.y; sy++) {
|
||||
for (int sz = 0; sz < size.z; sz++) {
|
||||
for (int sx = 0; sx < size.x; sx++) {
|
||||
auto pos = origin;
|
||||
pos += prevRotation.axisX * sx;
|
||||
pos += prevRotation.axisY * sy;
|
||||
pos += prevRotation.axisZ * sz;
|
||||
if (std::find(segmentBlocks.begin(), segmentBlocks.end(), pos) == segmentBlocks.end()) {
|
||||
set(pos.x, pos.y, pos.z, 0, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chunks::setRotation(int32_t x, int32_t y, int32_t z, uint8_t index) {
|
||||
if (index >= BlockRotProfile::MAX_COUNT) {
|
||||
return;
|
||||
}
|
||||
auto vox = get(x, y, z);
|
||||
if (vox == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto def = indices->getBlockDef(vox->id);
|
||||
if (!def->rotatable || vox->state.rotation == index) {
|
||||
return;
|
||||
}
|
||||
if (def->rt.extended) {
|
||||
setRotationExtended(def, vox->state, {x, y, z}, index);
|
||||
} else {
|
||||
vox->state.rotation = index;
|
||||
auto chunk = getChunkByVoxel(x, y, z);
|
||||
chunk->setModifiedAndUnsaved();
|
||||
}
|
||||
}
|
||||
|
||||
void Chunks::set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state) {
|
||||
if (y < 0 || y >= CHUNK_H) {
|
||||
return;
|
||||
}
|
||||
int32_t gx = x;
|
||||
int32_t gz = z;
|
||||
x -= ox * CHUNK_W;
|
||||
z -= oz * CHUNK_D;
|
||||
int cx = floordiv(x, CHUNK_W);
|
||||
int cz = floordiv(z, CHUNK_D);
|
||||
if (cx < 0 || cz < 0 || cx >= int(w) || cz >= int(d))
|
||||
if (cx < 0 || cz < 0 || cx >= static_cast<int>(w) || cz >= static_cast<int>(d)) {
|
||||
return;
|
||||
}
|
||||
Chunk* chunk = chunks[cz * w + cx].get();
|
||||
if (chunk == nullptr)
|
||||
if (chunk == nullptr) {
|
||||
return;
|
||||
}
|
||||
int lx = x - cx * CHUNK_W;
|
||||
int lz = z - cz * CHUNK_D;
|
||||
|
||||
// block finalization
|
||||
voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx];
|
||||
auto def = contentIds->getBlockDef(vox.id);
|
||||
if (def->inventorySize == 0)
|
||||
auto prevdef = indices->getBlockDef(vox.id);
|
||||
if (prevdef->inventorySize == 0) {
|
||||
chunk->removeBlockInventory(lx, y, lz);
|
||||
}
|
||||
if (prevdef->rt.extended && !vox.state.segment) {
|
||||
eraseSegments(prevdef, vox.state, gx, y, gz);
|
||||
}
|
||||
|
||||
// block initialization
|
||||
auto newdef = indices->getBlockDef(id);
|
||||
vox.id = id;
|
||||
vox.state = state;
|
||||
chunk->setModifiedAndUnsaved();
|
||||
if (!state.segment && newdef->rt.extended) {
|
||||
repairSegments(newdef, state, gx, y, gz);
|
||||
}
|
||||
|
||||
if (y < chunk->bottom) chunk->bottom = y;
|
||||
else if (y + 1 > chunk->top) chunk->top = y + 1;
|
||||
@ -242,14 +431,14 @@ voxel* Chunks::rayCast(
|
||||
if (voxel == nullptr){
|
||||
return nullptr;
|
||||
}
|
||||
const Block* def = contentIds->getBlockDef(voxel->id);
|
||||
const auto def = indices->getBlockDef(voxel->id);
|
||||
if (def->selectable){
|
||||
end.x = px + t * dx;
|
||||
end.y = py + t * dy;
|
||||
end.z = pz + t * dz;
|
||||
iend.x = ix;
|
||||
iend.y = iy;
|
||||
iend.z = iz;
|
||||
iend.x = ix;
|
||||
iend.y = iy;
|
||||
iend.z = iz;
|
||||
|
||||
if (!def->rt.solid) {
|
||||
const std::vector<AABB>& hitboxes = def->rotatable
|
||||
@ -261,7 +450,14 @@ voxel* Chunks::rayCast(
|
||||
|
||||
bool hit = false;
|
||||
|
||||
for (const auto& box : hitboxes) {
|
||||
glm::vec3 offset {};
|
||||
if (voxel->state.segment) {
|
||||
offset = seekOrigin(iend, def, voxel->state) - iend;
|
||||
}
|
||||
|
||||
for (auto box : hitboxes) {
|
||||
box.a += offset;
|
||||
box.b += offset;
|
||||
scalar_t boxDistance;
|
||||
glm::ivec3 boxNorm;
|
||||
if (ray.intersectAABB(iend, box, maxDist, boxNorm, boxDistance) > RayRelation::None && boxDistance < distance) {
|
||||
@ -359,7 +555,7 @@ glm::vec3 Chunks::rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDis
|
||||
if (voxel == nullptr) {
|
||||
return glm::vec3(px + t * dx, py + t * dy, pz + t * dz);
|
||||
}
|
||||
const Block* def = contentIds->getBlockDef(voxel->id);
|
||||
const auto def = indices->getBlockDef(voxel->id);
|
||||
if (def->obstacle) {
|
||||
if (!def->rt.solid) {
|
||||
const std::vector<AABB>& hitboxes = def->rotatable
|
||||
@ -370,9 +566,14 @@ glm::vec3 Chunks::rayCastToObstacle(glm::vec3 start, glm::vec3 dir, float maxDis
|
||||
glm::ivec3 norm;
|
||||
Ray ray(start, dir);
|
||||
|
||||
glm::ivec3 offset {};
|
||||
if (voxel->state.segment) {
|
||||
offset = seekOrigin({ix, iy, iz}, def, voxel->state) - glm::ivec3(ix, iy, iz);
|
||||
}
|
||||
|
||||
for (const auto& box : hitboxes) {
|
||||
// norm is dummy now, can be inefficient
|
||||
if (ray.intersectAABB(glm::ivec3(ix, iy, iz), box, maxDist, norm, distance) > RayRelation::None) {
|
||||
if (ray.intersectAABB(glm::ivec3(ix, iy, iz)+offset, box, maxDist, norm, distance) > RayRelation::None) {
|
||||
return start + (dir * glm::vec3(distance));
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,10 +17,15 @@ class ContentIndices;
|
||||
class Chunk;
|
||||
class WorldFiles;
|
||||
class LevelEvents;
|
||||
class Block;
|
||||
|
||||
/* Player-centred chunks matrix */
|
||||
class Chunks {
|
||||
const ContentIndices* const contentIds;
|
||||
const ContentIndices* const indices;
|
||||
|
||||
void eraseSegments(const Block* def, blockstate state, int x, int y, int z);
|
||||
void repairSegments(const Block* def, blockstate state, int x, int y, int z);
|
||||
void setRotationExtended(Block* def, blockstate state, glm::ivec3 origin, uint8_t rotation);
|
||||
public:
|
||||
std::vector<std::shared_ptr<Chunk>> chunks;
|
||||
std::vector<std::shared_ptr<Chunk>> chunksSecond;
|
||||
@ -40,11 +45,32 @@ public:
|
||||
|
||||
Chunk* getChunk(int32_t x, int32_t z);
|
||||
Chunk* getChunkByVoxel(int32_t x, int32_t y, int32_t z);
|
||||
voxel* get(int32_t x, int32_t y, int32_t z);
|
||||
voxel* get(int32_t x, int32_t y, int32_t z) const;
|
||||
|
||||
inline voxel* get(glm::ivec3 pos) {
|
||||
return get(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
light_t getLight(int32_t x, int32_t y, int32_t z);
|
||||
ubyte getLight(int32_t x, int32_t y, int32_t z, int channel);
|
||||
void set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state);
|
||||
|
||||
/// @brief Seek for the extended block origin position
|
||||
/// @param pos segment block position
|
||||
/// @param def segment block definition
|
||||
/// @param state segment block state
|
||||
/// @return origin block position or `pos` if block is not extended
|
||||
glm::ivec3 seekOrigin(glm::ivec3 pos, const Block* def, blockstate state);
|
||||
|
||||
/// @brief Check if required zone is replaceable
|
||||
/// @param def definition of the block that requires a replaceable zone
|
||||
/// @param state the block state
|
||||
/// @param coord position of the zone start
|
||||
/// @param ignore ignored block id (will be counted as replaceable)
|
||||
bool checkReplaceability(const Block* def, blockstate state, glm::ivec3 coord, blockid_t ignore=0);
|
||||
|
||||
void setRotation(int32_t x, int32_t y, int32_t z, uint8_t rotation);
|
||||
|
||||
voxel* rayCast(
|
||||
glm::vec3 start,
|
||||
glm::vec3 dir,
|
||||
|
||||
@ -11,25 +11,27 @@ inline constexpr int BLOCK_DIR_UP = 0x4;
|
||||
inline constexpr int BLOCK_DIR_DOWN = 0x5;
|
||||
|
||||
struct blockstate {
|
||||
uint8_t rotation : 3;
|
||||
uint8_t segment : 2; // planned to 0.22
|
||||
uint8_t reserved : 3;
|
||||
uint8_t userbits : 8;
|
||||
uint8_t rotation : 3; // block rotation index
|
||||
uint8_t segment : 3; // segment block bits
|
||||
uint8_t reserved : 2; // reserved bits
|
||||
uint8_t userbits : 8; // bits for use in block script
|
||||
};
|
||||
static_assert (sizeof(blockstate) == 2);
|
||||
|
||||
/// @brief blockstate cast to an integer (optimized out in most cases)
|
||||
inline constexpr blockstate_t blockstate2int(blockstate b) {
|
||||
return static_cast<blockstate_t>(b.rotation) |
|
||||
static_cast<blockstate_t>(b.segment) << 3 |
|
||||
static_cast<blockstate_t>(b.reserved) << 5 |
|
||||
static_cast<blockstate_t>(b.reserved) << 6 |
|
||||
static_cast<blockstate_t>(b.userbits) << 8;
|
||||
}
|
||||
|
||||
/// @brief integer cast to a blockstate (optimized out in most cases)
|
||||
inline constexpr blockstate int2blockstate(blockstate_t i) {
|
||||
return {
|
||||
static_cast<uint8_t>(i & 0b111),
|
||||
static_cast<uint8_t>((i >> 3) & 0b11),
|
||||
static_cast<uint8_t>((i >> 5) & 0b111),
|
||||
static_cast<uint8_t>((i >> 3) & 0b111),
|
||||
static_cast<uint8_t>((i >> 6) & 0b11),
|
||||
static_cast<uint8_t>((i >> 8) & 0xFF)
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user