Skip to content

Releases: serg06/mc2

ImGui, multi-threaded chunk generation, and code cleanup.

02 Jul 01:26
Compare
Choose a tag to compare

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.
  • 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!

28 May 18:00
Compare
Choose a tag to compare

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

24 May 02:44
Compare
Choose a tag to compare

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!

12 Jan 07:14
Compare
Choose a tag to compare

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 with cv.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!

06 Jan 15:06
Compare
Choose a tag to compare

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

17 Dec 18:48
Compare
Choose a tag to compare

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

09 Dec 19:54
Compare
Choose a tag to compare

New features:

  • Block placement and destruction.
  • Highlight blocks we're looking at.
  • 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)

04 Dec 09:36
Compare
Choose a tag to compare

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.
  • 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

01 Dec 09:12
61f1867
Compare
Choose a tag to compare

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

30 Nov 06:26
Compare
Choose a tag to compare

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