Releases: serg06/mc2
ImGui, multi-threaded chunk generation, and code cleanup.
New features:
- There's a main menu.
- There's an ESC menu (press ESC in game.)
- Chunk generation happens on a separate thread so the game should feel a lot smoother.
Controls:
- F11 fullscreen
- R toggle mouse raw input
- T toggle t-junction fixing
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC open main menu
- N toggle noclip
- P cycle polygon mode
- [+ or numpad+] (press once or hold) increase render distance
- [- or numpad-] (press once or hold) decrease render distance (without unloading existing chunks)
- C toggle face culling
- Left-click destroy block
- Right-click place flowing water
- F3 enable debug overlay
- Scroll change block type (active block type visible in debug menu)
Technical details:
- Used ImGui for my GUI.
- Replaced my custom text-rendering shaders with ImGui.
- All zmq messages are now sent across a single message bus.
- Pros:
- Much simpler code. Every class gets a single BusNode object that they send/receives all messages through.
- Cons:
- If a thread can't keep up and their incoming message queue overflows, they'll lose some messages.
- Have to manually make sure any pointers I send are received, dereferenced, and deleted by exactly one thread.
- Takes up a lot of RAM - I had to give all sockets huge incoming/outgoing message queues so that they don't miss any messages.
- Pros:
- Ton of code cleanup
- Moved all .h code into .cpp
- Split complex classes into multiple ones
- Focused on splitting away the rendering from the world data
- Replaced a ton of C-style pointers with std::shared_ptr
What's next:
- Perlin worms for cave generation
- Separating the code more (I'd like to handle all GUI-related rendering in a separate class)
- Pause world when in ESC menu
- Importing and parsing Minecraft's model/texture files so that I can render all their assets without much repetitive manual work.
Huge meshing speed boost! Crazy performance!
New features:
- Meshing is now insanely fast. Try holding +, the world will expand at supersonic speeds.
Technical details:
- Read about ZeroMQ and replaced my mutexes with a rough messaging system.
- Create a unique_queue which maps keys to elements, and given a key, can move the corresponding element to the front or back of the queue in O(1).
- Came up with a really cool algorithm for sharing world data with other threads without worrying about it being edited. (See extra notes at the bottom.)
Controls:
- F11 fullscreen
- R toggle mouse raw input
- T toggle t-junction fixing
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip
- P cycle polygon mode
- [+ or numpad+] (press once or hold) increase render distance
- [- or numpad-] (press once or hold) decrease render distance (without unloading existing chunks)
- C toggle face culling
- Left-click destroy block
- Right-click place flowing water
- F3 enable debug overlay
- Scroll change block type (active block type visible in debug menu)
Extra notes:
When you hear "meshing speed boost" you may think that my meshing algorithm has been improved, but no; it's the inter-thread communication that has improved. My main thread and meshing thread (only one for now) communicate much more efficiently.
Old way:
-
Main thread creates a "mesh generation request" containing A COPY of a chunk of world data that needs meshing.
-
Main thread locks the "request queue" and sticks the request in there.
-
Meshing thread picks it up, creates the "mesh generation response", and sticks in in the "response queue."
-
Main thread picks it up and updates its list of meshes.
New way:
-
Main thread creates a "mesh generation request" but now with A CONST REFERENCE to the block data.
-
Main thread asynchronously sends the request message through zeromq (basically a super-fast socket) to the meshing thread.
-
Meshing thread receives the request, removes duplicate requests using an O(1) unique_queue data structure, generates the mesh, and sends it back through zeromq.
-
Main thread asynchronously receives mesh and updates its list of meshes.
Without locks and conditional variables, everything is much smoother!
Now you may be thinking "How can you send a reference to the world data? What if someone else modifies that data while you're meshing it?" I came up with a clever way to overcome that problem:
-
The world stores data as shared pointers to chunks of blocks.
-
When creating the "mesh generation request", the world simply duplicates the shared pointers to the data, increasing the reference count of the shared pointers until the meshing thread discards it.
-
When placing or breaking a block into a chunk of data, the world checks the reference count of the shared pointer - if it's 1 (only the world has a copy), the world simply edits the existing data. If it's more than 1 (at least one meshing request is somewhere in the request queue), the world creates a copy of the data and discards the old shared pointer.
Now you may be thinking "What are the downsides of this strategy compared to the other one?"
- So far, none. Using the old method, every time we placed/destroyed a block we would create a copy of the block's chunk data. Now, we only create a copy if the chunk is currently being meshed.
Stability, smoothness, breakage
New features:
- Water is drawn just like in Minecraft! That's right; even when looking through 10 layers of water, it will look like there's just 1 layer.
- Make see-through leaves a little nicer.
- Fix crashing and texture bugging when growing world too quickly.
- Smoother experience because new chunks are only queued for generation when crossing a chunk border.
Technical details:
- Read a book on modern CMake and greatly improved my project. Now generating it is as simple as
mkdir build && cd build && build ..
, and you can even compile it from the command line! - Code a little more organized with an FBO class.
- Store each minichunk's meshes in a FBO instead of in a bunch of separate VBOs.
- Draw water opaquely then merge it over top of the rest of the world. This keeps water rendering consistently regardless of which minichunk is drawn first.
- Attempt to manually reduce some branching.
- Fix crashing and texture bugging when growing world too quickly. It was a multi-threading issue (two threads accessing an std::unordered_map at the same time); I had to add some locks to fix it, which unfortunately slowed down the program.
- Smoother experience because chunk pointers are now stored/access contiguously in memory when rendering.
Controls:
- F11 fullscreen
- R toggle mouse raw input
- T toggle t-junction fixing
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip
- P cycle polygon mode
- [+ or numpad+] (press once or hold) increase render distance
- [- or numpad-] (press once or hold) decrease render distance (without unloading existing chunks)
- C toggle face culling
- Left-click destroy block
- Right-click place flowing water
- F3 enable debug overlay
- Scroll change block type (active block type visible in debug menu)
Decrease memory usage 7x!
New features:
- Memory footprint is 7x smaller. Can reach 100 render distance on 32-bit compilation!
- Chunk generation is much faster (don't need to allocate so much space).
- Fix movement bug that was causing snapping-to-edges for the past month.
- No longer crashes when minimizing / alt-tabbing.
- Prioritize meshing existing chunks over new ones.
- Adding/destroying blocks will look good even while other chunks are queued up to be meshed.
- Prettier and scarier underwater tint.
- No more background pixels shining through terrain (unless looking through water.)
- Smoother mouse.
- Disable raw mouse input by default. (Can toggle with R.)
Technical details:
- Represent world using interval maps instead of arrays.
- Chunk meshing tiny bit slower because accessing arrays is O(1) but accessing interval maps is O(log(num intervals)).
- Starting window dimensions can now be anything, instead of only 800/600.
- Wake up threads with the proper amount of
cv.notify_one()
instead of shotgunning withcv.notify_all()
. - T-junctions are now fixed by filling in all pixels with max depth (1.0) except for ones where we drew a transparent pixel (e.g. leaves).
- Clean up old code by making variables/functions const and passing classes by reference.
Controls:
- F11 fullscreen
- R toggle mouse raw input
- T toggle t-junction fixing
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip
- P cycle polygon mode
- [+ or numpad+] (press once or hold) increase render distance
- [- or numpad-] (press once or hold) decrease render distance (without unloading existing chunks)
- C toggle face culling
- Left-click destroy block
- Right-click place flowing water
- F3 enable debug overlay
- Scroll change block type (active block type visible in debug menu)
Everything's a lot smoother - and faster!
New features:
- Debug overlay can be enabled with F3
- Water now flows into empty spaces - try it out! (right click)
- "Deep blue water" effect when underwater
- Much smoother mouse, chunk loading, block destruction, mesh generation, loading mesh to gpu
- Significantly higher FPS
- Block textures are no longer see-through when viewed at an extreme angle
- Far-away blocks are much less grainy now
- Fix tiny pixels / pixel-lines shining through in dark areas
- Fixed a crash caused by OBS hooking onto our program
Controls:
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip
- P cycle polygon mode
- [+ or numpad+] (press once or hold) increase render distance
- [- or numpad-] (press once or hold) decrease render distance (without unloading existing chunks)
- C toggle face culling
- Left-click destroy block
- Right-click place flowing water
- F3 enable debug overlay
- Scroll change block type (active block type visible in debug menu)
Technical details (not very interesting):
- Created a program for drawing text on the screen
- splits viewport into evenly-sized grid of letters
- horizontally, always fit a pre-defined number of letters
- vertically, scale size to keep the grid elements square
- Speed up drawing by rendering quads as triangle strips
- Switched to
GL_NEAREST
min-filtering in order to prevent partially-transparent from affecting depth buffer at extreme angles - Add mipmapping to counter the grainyness of
GL_NEAREST
- Create my own mipmap function which doesn't create translucent pixels like OpenGL's does
- The downside: Far-away leaves are less see-through.
- The upside: Far-away blocks look way smoother than in Minecraft.
- Fix the t-junctions caused by greedy meshing by running a t-junction filter over my render.
- For any pixels whose depth wasn't written to, fill it in using its neighboring colors/depths.
- Add precompile header
- Much smoother mouse, chunk loading, block destruction, mesh generation, loading mesh to gpu
- Mouse is smoother because we're doing less every loop
- Chunk loading is smoother because of smarter/faster algorithms
- Block destruction is smoother because
a. we're re-generating meshes for the minimum number of blocks
b. our mesh-generation thread generates it now instead of us
c. our mesh-generation thread is immediately woken up with a conditional variable, instead of having it sleep and loop. - Mesh generation is smoother because of smarter/faster algorithms
- Loading mesh to GPU is faster because I put all quads on one buffer now (instead of 5), and I do it all with a single std::copy call.
- Fixed a crash caused by OBS hooking the program, by making sure the default framebuffer is bound to
GL_DRAW_BUFFER
before I swap buffers. - Added more per-voxel data so we now can't handle more than a 47 render distance.
- Gonna try to fix this by storing data in interval maps.
HUGE fps boosts - and a lot of great experience
New features:
- Huge (3x) FPS boost because of a super-efficient frustum culling.
- Huge (10x) chunk generation speed boost because of multithreading plus a better minichunk-layer-extraction algorithm.
- Much smoother chunk generation.
- All blocks have Minecraft textures now.
- Translucent water.
- Blue tint underwater.
- Support for partially-transparent blocks (e.g. leaves).
- Improved tree generation.
- Blocks now load in a circle around you, instead of in a diamond.
Technical details:
- In order to make water transparent, first draw all non-water blocks, then draw all water blocks on top. This solution won't work if I add other partially-translucent blocks to the game.
- Speed up fresh-buffer creation by mapping to buffer with
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT
and generating data directly inside. - Tested frustum culling on the GPU. It was slower because of the asynchronous nature of GPU calls.
- Tested meshing on the GPU. It was slower because I couldn't come up with a good parallel algorithm. (I was doing one thread per 16x16 layer, but what I need is an algorithm that uses 256 threads per 16x16 layers.)
- Reduced .exe size by setting some library flags.
Controls:
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip
- P cycle polygon mode
- [+ or numpad+] increase render distance
- [- or numpad-] decrease render distance (without unloading existing chunks)
- C toggle face culling
- Left-click destroy block
- Right-click place diamond block
More Minecrafty
New features:
- Block placement and destruction.
- Highlight blocks we're looking at.
- Implement an efficient ray-casting algorithm
- Better map generation.
- Simple trees and water.
- Small cliffs.
- Textured grass blocks.
- New textures are easy to add now, but I kinda like this simple look.
Controls:
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip
- P cycle polygon mode
- [+ or numpad+] increase render distance
- [- or numpad-] decrease render distance (without unloading existing chunks)
- C toggle face culling
- Left-click destroy block
- Right-click place grass block
HUGE FPS INCREASE (50x)
New features:
- Enormous FPS increase.
- Generate a quad mesh for every mini-chunk.
- Instead of drawing every cube on a mini-chunk (4096 cubes * 6 sides => 50,000 triangles), can combine nearby cubes to create rectangles, and can draw one rectangle instead of multiple cubes.
- Currently cuts ~50,000 draws down to 40. Not 40,000, but 40.
- It's easy to see the difference by using P. Open the last version and hit P to see triangle lines. Then open this version and compare.
- Generate a quad mesh for every mini-chunk.
- New controls.
Controls:
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip
- P cycle polygon mode
- [+ or numpad+] increase render distance
- [- or numpad-] decrease render distance (without unloading existing chunks)
- C toggle face culling
Huge FPS increase (100x) => can easily draw bigger maps now
New Features:
- Huge FPS increase.
- Split chunks into 16x16x16 mini-chunks.
- Draw mini-chunks separately.
- Don't draw mini-chunks that are surrounded on all sides.
- Don't draw mini-chunks that consist of only air.
- Impromptu fake skybox.
- New controls.
Controls:
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip
- P cycle polygon mode
- [numpad+] increase render distance
- [numpad-] decrease render distance (without unloading existing chunks)
Automatic terrain generation
New Features:
- Basic automatic world generation.
- Go in any direction, and the world generates around you.
- Used FastNoise for Simplex noise generation.
- Shaders and app framework can now handle multiple chunks.
- Fixed blocks flickering when farther away.
Controls:
- Mouse look around
- WASD move around
- Shift/space move up/down
- ESC quit
- N toggle noclip