diff --git a/src/block.cpp b/src/block.cpp index 1cfae5f..119b295 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -7,6 +7,7 @@ const std::unordered_map Block::top_texture_names = { { Block::StillWater, "water_square" }, { Block::OakWood, "tree_top" }, { Block::OakLeaves, "leaves" }, + { Block::DiamondBlock, "blockDiamond" }, { Block::Outline, "outline" }, }; @@ -16,6 +17,7 @@ const std::unordered_map Block::bottom_texture_names { Block::StillWater, "water_square" }, { Block::OakWood, "tree_top" }, { Block::OakLeaves, "leaves" }, + { Block::DiamondBlock, "blockDiamond" }, { Block::Outline, "outline" }, }; @@ -25,6 +27,7 @@ const std::unordered_map Block::side_texture_names = { Block::StillWater, "water_square" }, { Block::OakWood, "tree_side" }, { Block::OakLeaves, "leaves" }, + { Block::DiamondBlock, "blockDiamond" }, { Block::Outline, "outline" }, }; diff --git a/src/chunk.cpp b/src/chunk.cpp index 918bf80..86b542f 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -58,7 +58,6 @@ inline float softmax(float v, float minv, float maxv) { } Chunk* gen_chunk_data(int chunkX, int chunkZ) { - char buf[256]; FastNoise fn; // create chunk diff --git a/src/chunkdata.h b/src/chunkdata.h index 857af12..cc7b821 100644 --- a/src/chunkdata.h +++ b/src/chunkdata.h @@ -12,11 +12,16 @@ using namespace vmath; -static inline ivec4 clamp_coords_to_world(ivec4 coords) { - coords[1] = std::clamp(coords[1], 0, BLOCK_MAX_HEIGHT); - return coords; + +static inline ivec4 clamp_coords_to_world(const ivec4 &coords) { + return { coords[0], std::clamp(coords[1], 0, BLOCK_MAX_HEIGHT), coords[2], 0 }; +} + +static inline ivec3 clamp_coords_to_world(const ivec3 &coords) { + return { coords[0], std::clamp(coords[1], 0, BLOCK_MAX_HEIGHT), coords[2] }; } + // Chunk Data is always stored as width wide and depth deep class ChunkData { public: diff --git a/src/game.cpp b/src/game.cpp index a3220e6..422f0fd 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -21,8 +21,6 @@ #include // TODO: Upgrade version, or use better library? #include -#define NUM_MESH_GEN_THREADS 1 - // 1. TODO: Apply C++11 features // 2. TODO: Apply C++14 features // 3. TODO: Apply C++17 features @@ -35,13 +33,12 @@ using namespace std; using namespace vmath; namespace { - static bool stop = new bool; + static bool stop = false; } // Windows main int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { - stop = false; glfwSetErrorCallback(glfw_onError); App::app = new App(); App::app->run(); @@ -125,7 +122,6 @@ void App::startup() { memset(held_keys, false, sizeof(held_keys)); glfwGetCursorPos(window, &last_mouse_x, &last_mouse_y); // reset mouse position world = new World(); - world->glInfo = &glInfo; // prepare opengl setup_opengl(&glInfo); @@ -240,8 +236,6 @@ void App::render(float time) { // update player's movement based on how much time has passed since we last did it void App::update_player_movement(const float dt) { - char buf[256]; - /* VELOCITY FALLOFF */ // TODO: Handle walking on blocks, in water, etc. Maybe do it based on friction. @@ -584,7 +578,7 @@ void App::onMouseButton(int button, int action) { // if we're not in the way, place it if (result == end(intersecting_blocks)) { - world->add_block(desired_position, Block::Grass); + world->add_block(desired_position, Block::DiamondBlock); } } } diff --git a/src/game.h b/src/game.h index d6b23ee..de98d18 100644 --- a/src/game.h +++ b/src/game.h @@ -2,6 +2,7 @@ #define __GAME_H__ #define GPU_MAX_CHUNKS 256 +#define NUM_MESH_GEN_THREADS 1 #include "chunk.h" #include "chunkdata.h" diff --git a/src/render.cpp b/src/render.cpp index 27a8457..c46ad83 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -302,10 +302,7 @@ namespace { float* red = new float[16 * 16 * MAX_BLOCK_TYPES * 4]; for (int i = 0; i < 16 * 16 * MAX_BLOCK_TYPES; i++) { - red[i * 4 + 0] = 1.0f; // R - red[i * 4 + 1] = 0.0f; // G - red[i * 4 + 2] = 0.0f; // B - red[i * 4 + 3] = 1.0f; // A + ((vec4*)red)[i] = { 1.0f, 0.0f, 0.0f, 1.0f }; } // set all textures as BRIGHT RED, so we know when something's wrong diff --git a/src/world.cpp b/src/world.cpp index e23844d..f3ef022 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -312,6 +312,9 @@ MiniChunkMesh* World::gen_minichunk_mesh(MiniChunk* mini) { } } + // TODO: rotate texture sides the correct way. (It's noticeable when placing down diamond block.) + // -> Or alternatively, can just rotate texture lmao. + // convert quads back to 3D coordinates vector quads = quads_2d_3d(quads2d, layers_idx, i, face); diff --git a/src/world.h b/src/world.h index 529ad00..ac489dc 100644 --- a/src/world.h +++ b/src/world.h @@ -62,25 +62,62 @@ class World { // map of (chunk coordinate) -> chunk unordered_map chunk_map; - int rendered = 0; // get_chunk cache Chunk* chunk_cache[5] = { nullptr, nullptr, nullptr, nullptr, nullptr }; vmath::ivec2 chunk_cache_ivec2[5] = { ivec2(INT_MAX), ivec2(INT_MAX), ivec2(INT_MAX), ivec2(INT_MAX), ivec2(INT_MAX) }; int chunk_cache_clock_hand = 0; // clock cache - OpenGLInfo *glInfo; - GLsync chunk_sync = NULL; - //queue chunk_gen_queue; - // minis currently having their meshes generated - queue chunk_gen_mini_queue; // todo: make this ivec3 instead? - vector chunk_gen_minis; // todo: make this ivec3 instead? - std::mutex chunk_gen_mini_mutex; - std::chrono::time_point last_chunk_sync_check = std::chrono::high_resolution_clock::now(); + // multi-thread-access minis who need their mesh generated + queue mesh_gen_queue; // storage + unordered_set mesh_gen_set; // uniqueness + std::mutex mesh_gen_mutex; // thread-safety + + // count how many times render() has been called + int rendered = 0; World() { } + // enqueue mesh generation of this mini + // expects mesh lock + inline void enqueue_mesh_gen(MiniChunk* mini) { + assert(mesh_gen_set.size() == mesh_gen_queue.size() && "wew"); + assert(mini != nullptr && "seriously?"); + + // check if mini in set + auto search = mesh_gen_set.find(mini); + + // already in set, so don't add, just quit + if (search != mesh_gen_set.end()) { + return; + } + + // not in set yet, add. + mesh_gen_set.insert(mini); + mesh_gen_queue.push(mini); + } + + inline MiniChunk* dequeue_mesh_gen() { + assert(mesh_gen_set.size() == mesh_gen_queue.size() && "wew"); + + // no meshes to generate! + if (mesh_gen_set.size() == 0) { + return nullptr; + } + + // get mini + MiniChunk* mini = mesh_gen_queue.front(); + assert(mini != nullptr && "seriously?"); + + // remove it from set and queue + mesh_gen_queue.pop(); + mesh_gen_set.erase(mini); + + // done + return mini; + } + // add chunk to chunk coords (x, z) inline void add_chunk(int x, int z, Chunk* chunk) { ivec2 coords = { x, z }; @@ -110,18 +147,6 @@ class World { //OutputDebugString(buf); } - // get chunk (generate it if required) - inline Chunk* get_chunk_generate_if_required(int x, int z) { - auto search = chunk_map.find({ x, z }); - - // if doesn't exist, generate it - if (search == chunk_map.end()) { - gen_chunk(x, z); - } - - return chunk_map[{x, z}]; - } - // get multiple chunks -- much faster than get_chunk_generate_if_required when n > 1 inline std::unordered_set get_chunks_generate_if_required(vector chunk_coords) { // don't wanna get duplicates @@ -157,6 +182,7 @@ class World { } } + // generate all chunks (much faster than gen_chunk) inline void gen_chunks(vector to_generate) { std::unordered_set set; for (auto coords : to_generate) { @@ -165,10 +191,9 @@ class World { return gen_chunks(set); } - // generate chunk at (x, z) and add it + // generate all chunks (much faster than gen_chunk) inline void gen_chunks(std::unordered_set to_generate) { // get pointers ready - // TODO: vector<>. vector chunks(to_generate.size()); // generate chunks and set pointers @@ -222,45 +247,11 @@ class World { } // add them all to queue - chunk_gen_mini_mutex.lock(); + mesh_gen_mutex.lock(); for (auto &mini : minis_to_mesh) { - chunk_gen_mini_queue.push(mini); - //char buf[256]; - //sprintf(buf, "Adding mini (%d, %d, %d) to queue...\n", mini->coords[0], mini->coords[1], mini->coords[2]); - //OutputDebugString(buf); + enqueue_mesh_gen(mini); } - chunk_gen_mini_mutex.unlock(); - - - // generate them all - //for (auto chunk : to_generate_minis) { - // for (auto &mini : chunk->minis) { - // mini.invisible = mini.invisible || mini.all_air() || check_if_covered(mini); - - // if (!mini.invisible) { - // MiniChunkMesh* mesh = gen_minichunk_mesh(&mini); - - // MiniChunkMesh* non_water = new MiniChunkMesh; - // MiniChunkMesh* water = new MiniChunkMesh; - - // for (auto &quad : mesh->quads3d) { - // if ((Block)quad.block == Block::StillWater) { - // water->quads3d.push_back(quad); - // } - // else { - // non_water->quads3d.push_back(quad); - // } - // } - - // assert(mesh->size() == non_water->size() + water->size()); - - // mini.mesh = non_water; - // mini.water_mesh = water; - - // mini.update_quads_buf(); - // } - // } - //} + mesh_gen_mutex.unlock(); } // get chunk or nullptr (using cache) (TODO: LRU?) @@ -369,6 +360,7 @@ class World { } // get a block's type + // inefficient when called repeatedly - if you need multiple blocks from one mini/chunk, use get_mini (or get_chunk) and mini.get_block. inline Block get_type(int x, int y, int z) { Chunk* chunk = get_chunk_containing_block(x, z); @@ -383,13 +375,6 @@ class World { inline Block get_type(vmath::ivec3 xyz) { return get_type(xyz[0], xyz[1], xyz[2]); } inline Block get_type(vmath::ivec4 xyz_) { return get_type(xyz_[0], xyz_[1], xyz_[2]); } - // generate chunk at (x, z) and add it - inline void gen_chunk(int x, int z) { - vector coords; - coords.push_back({ x, z }); - gen_chunks(coords); - } - inline bool check_if_covered(MiniChunk &mini) { // if contains any translucent blocks, don't know how to handle that yet if (mini.any_translucent()) { @@ -397,40 +382,36 @@ class World { } // none are air, so only check outside blocks - for (int miniX = 0; miniX < CHUNK_WIDTH; miniX++) { - for (int miniY = 0; miniY < MINICHUNK_HEIGHT; miniY++) { - for (int miniZ = 0; miniZ < CHUNK_DEPTH; miniZ++) { - int x = mini.coords[0] * CHUNK_WIDTH + miniX; - int y = mini.coords[1] + miniY; - int z = mini.coords[2] * CHUNK_DEPTH + miniZ; - - vmath::ivec4 coords = vmath::ivec4(x, y, z, 0); + for (int miniY = 0; miniY < MINICHUNK_HEIGHT; miniY++) { + for (int miniZ = 0; miniZ < CHUNK_DEPTH; miniZ++) { + for (int miniX = 0; miniX < CHUNK_WIDTH; miniX++) { + vmath::ivec3 coords = { mini.coords[0] * CHUNK_WIDTH + miniX, mini.coords[1] + miniY, mini.coords[2] * CHUNK_DEPTH + miniZ }; // if along east wall, check east if (miniX == CHUNK_WIDTH - 1) { - if (get_type(clamp_coords_to_world(coords + IEAST_0)).is_translucent()) return false; + if (get_type(clamp_coords_to_world(coords + IEAST)).is_translucent()) return false; } // if along west wall, check west if (miniX == 0) { - if (get_type(clamp_coords_to_world(coords + IWEST_0)).is_translucent()) return false; + if (get_type(clamp_coords_to_world(coords + IWEST)).is_translucent()) return false; } // if along north wall, check north if (miniZ == 0) { - if (get_type(clamp_coords_to_world(coords + INORTH_0)).is_translucent()) return false; + if (get_type(clamp_coords_to_world(coords + INORTH)).is_translucent()) return false; } // if along south wall, check south if (miniZ == CHUNK_DEPTH - 1) { - if (get_type(clamp_coords_to_world(coords + ISOUTH_0)).is_translucent()) return false; + if (get_type(clamp_coords_to_world(coords + ISOUTH)).is_translucent()) return false; } // if along bottom wall, check bottom if (miniY == 0) { - if (get_type(clamp_coords_to_world(coords + IDOWN_0)).is_translucent()) return false; + if (get_type(clamp_coords_to_world(coords + IDOWN)).is_translucent()) return false; } // if along top wall, check top if (miniY == MINICHUNK_HEIGHT - 1) { - if (get_type(clamp_coords_to_world(coords + IUP_0)).is_translucent()) return false; + if (get_type(clamp_coords_to_world(coords + IUP)).is_translucent()) return false; } } } @@ -440,8 +421,6 @@ class World { } inline void render(OpenGLInfo* glInfo, const vmath::vec4(&planes)[6]) { - char buf[256]; - // collect all the minis we're gonna draw vector minis_to_draw; for (auto &[coords_p, chunk] : chunk_map) { @@ -457,9 +436,12 @@ class World { // draw them glUseProgram(glInfo->rendering_program); + // draw non-water first for (auto &mini : minis_to_draw) { mini->render_meshes(glInfo); } + + // then water for (auto &mini : minis_to_draw) { mini->render_water_meshes(glInfo); } @@ -611,7 +593,7 @@ class World { MiniChunk* face_mini = mini; if (!in_range(face_coords, ivec3(0, 0, 0), ivec3(15, 15, 15))) { //gen_layer_slow(mini, layers_idx, layer_no, face, result); - auto face_mini_coords = mini->coords + (layers_idx == 1 ? face*16 : face); + auto face_mini_coords = mini->coords + (layers_idx == 1 ? face * 16 : face); face_mini = (face_mini_coords[1] < 0 || face_mini_coords[1] > BLOCK_MAX_HEIGHT - MINICHUNK_HEIGHT) ? nullptr : get_mini(face_mini_coords); } @@ -718,22 +700,6 @@ class World { return max_size; } - //// check if a block's face is visible - //inline bool is_face_visible(vmath::ivec4 block_coords, vmath::ivec4 axis, int backface) { - // return get_type(block_coords + face_to_direction(face)) == Block::Air; - //} - - // position you're at - // direction you're looking at - inline void render_outline_of_forwards_block(vec4 position, vec4 direction) { - - } - - //// todo: understand - //inline float intBound(s, ds) { - // return ds > 0 ? (Math.ceil(s) - s) / ds : (s - Math.floor(s)) / -ds; - //} - static inline float intbound(float s, float ds) { // Some kind of edge case, see: @@ -747,6 +713,8 @@ class World { } inline void highlight_block(OpenGLInfo* glInfo, GlfwInfo* windowInfo, int x, int y, int z) { + // TODO: Don't recreate buffer every frame! + // Figure out corners / block types Block blocks[6]; ivec3 corner1s[6]; @@ -878,13 +846,6 @@ class World { inline void highlight_block(OpenGLInfo* glInfo, GlfwInfo* windowInfo, ivec3 xyz) { return highlight_block(glInfo, windowInfo, xyz[0], xyz[1], xyz[2]); } - - // make sure a block isn't air - inline bool not_air(ivec3 block_coords, ivec3 face) { - return get_type(block_coords) != Block::Air; - } - - /** * Call the callback with (x,y,z,value,face) of all blocks along the line * segment from point 'origin' in vector direction 'direction' of length @@ -1077,7 +1038,7 @@ class World { ivec3 mini_coords = get_mini_relative_coords(x, y, z); mini->set_block(mini_coords, Block::Air); - // regenerate textures for all neighboring minis (TODO: This should be a maximum of 3 neighbors, since the block always has at least 3 sides inside its mini.) + // regenerate textures for all neighboring minis (TODO: This should be a maximum of 3 neighbors, since >=3 sides of the destroyed block are facing its own mini.) on_mini_update(mini, { x, y, z }); } @@ -1095,72 +1056,22 @@ class World { void add_block(ivec3 xyz, Block block) { return add_block(xyz[0], xyz[1], xyz[2], block); }; - // extract layer from output - // TODO: memcpy? - static void extract_layer(unsigned *output, unsigned layer_idx, unsigned global_face_idx, unsigned global_minichunk_idx, Block(&results)[16][16]) { - for (int v = 0; v < 16; v++) { - for (int u = 0; u < 16; u++) { - results[u][v] = (uint8_t)output[u + v * 16 + layer_idx * 16 * 16 + global_face_idx * 16 * 16 * 16 + global_minichunk_idx * 6 * 16 * 16 * 16]; - } - } - } - - // fill layer in output - // TODO: memcpy? - static void fill_layer(unsigned *output, unsigned layer_idx, unsigned global_face_idx, unsigned global_minichunk_idx, Block(&layer)[16][16]) { - for (int v = 0; v < 16; v++) { - for (int u = 0; u < 16; u++) { - output[u + v * 16 + layer_idx * 16 * 16 + global_face_idx * 16 * 16 * 16 + global_minichunk_idx * 6 * 16 * 16 * 16] = (uint8_t)layer[u][v]; - } - } - } - - // fill in missed layers for a minichunk's layers - void fill_missed_layers(unsigned *output, MiniChunk* mini, unsigned global_minichunk_idx) { - Block layer[16][16]; - - // for each face - for (int global_face_idx = 0; global_face_idx < 6; global_face_idx++) { - int local_face_idx = global_face_idx % 3; - bool backface = global_face_idx < 3; - - ivec3 face = ivec3(0, 0, 0); - face[local_face_idx] = backface ? -1 : 1; - - // working indices are always gonna be xy, xz, or yz. - int working_idx_1 = local_face_idx == 0 ? 1 : 0; - int working_idx_2 = local_face_idx == 2 ? 1 : 2; - - // index of layer to fill - // if backface, fill first layer (0), else fill last layer (15) - int layer_idx = backface ? 0 : 15; - - // fill layer - gen_layer(mini, local_face_idx, layer_idx, face, layer); - fill_layer(output, layer_idx, global_face_idx, global_minichunk_idx, layer); - } - - OutputDebugString(""); - } - // generate a minichunk mutex from queue - // TODO: make queue a queue+set combo, so only unique minis in there. bool gen_minichunk_mesh_from_queue(vec3 player_pos) { // lock queue lock - chunk_gen_mini_mutex.lock(); + mesh_gen_mutex.lock(); // if queue empty, return - if (chunk_gen_mini_queue.size() == 0) { - chunk_gen_mini_mutex.unlock(); + if (mesh_gen_queue.size() == 0) { + mesh_gen_mutex.unlock(); return false; } // get mini - MiniChunk *mini = chunk_gen_mini_queue.front(); - chunk_gen_mini_queue.pop(); + MiniChunk *mini = dequeue_mesh_gen(); // no longer need queue - chunk_gen_mini_mutex.unlock(); + mesh_gen_mutex.unlock(); // lock mini mini->mesh_lock.lock();