{{FULL_COURSE}} Final Project - Mini Minecraft Milestone 2


Overview ----- You will continue your implementation of Mini Minecraft, adding detail and/or efficiency to your existing project. Each group member may choose any one of the milestone features to implement; you are not beholden to implement the procedural terrain feature this milestone just because you implemented procedural terrain for Milestone 1. #### Again, please note that late days cannot be used for the final project milestones #### Supplied Code --------- You'll be working from the code your group wrote for milestone 1. However, we are supplying two image files so that one group member may apply textures to the terrain blocks, along with normal maps. [Click here to download the image files.](minecraft_textures_all.zip) Additional block types ---------------------- In addition to the `STONE`, `DIRT`, and `GRASS` block types defined in the base code, you should add the following blocks to your `BlockType` enum: * `LAVA` * `WATER` * `ICE` You may add block types beyond these requirements as you see fit. Help Log (5 points) ------- Maintain a log of all help you receive and resources you use. Make sure the date and time, the names of everyone you work with or get help from, and every URL you use, except as noted in the collaboration policy. Also briefly log your question, bug or the topic you were looking up/discussing. Ideally, you should also the answer to your question or solution to your bug. This will help you learn and provide a useful reference for future assignments and exams. This also helps us know if there is a topic that people are finding difficult. If you did not use external resources or otherwise receive help, please submit a help log that states you did not receive external help. You may submit your help log as an ASCII (plain) text file or as a PDF. Refer to the Policies section of the course web site for more specifications. Week 2 Milestone Features (75 points each, Due Friday, April 17 at 11:59 PM) ------- For the second milestone of Mini Minecraft, each group member will implement a feature that adds detail or efficiency to your existing game engine. As with the first milestone, each group will present their completed work in the form of a pre-recorded video submitted alongside their code. This video must be no longer than four minutes in length, and it must show each feature implemented as it appears when the program is run. Only footage of your game is required; you do not need to record voice-over if you prefer to add text annotations to the video instead. The three main milestone features are: * Procedural generation of rivers using L-systems * Texturing and texture animation in OpenGL * Multithreading of terrain loading ### L-System Rivers ### You will implement two main features: * Create procedural rivers in your terrain using L-Systems * Introduce a post-process rendering pipeline into your game engine to render an overlay when the player's head is beneath water Using a 2D L-system grammar structure, design a set of functions to procedurally generate a branching river that fits within one "terrain generation zone" as described in the base code (i.e. a 4x4 `Chunk` area). Your rivers should have the following features: * Incorporates random numbers to influence path orientation and probability of branch generation * When a river branches its "child rivers" should be thinner than the parent branch * River segments should not be totally linear; incorporate curvature to give them a semblance of naturality * Riverbed should be hemispherical in shape, and it should carve out the surrounding terrain to allow easy placement of water
Uncarved Terrain
Carved Terrain
Delta River

Source
Once you've generated a river, you should project it into your terrain so that the `WATER` blocks it consists of rest at a height of 128. As mentioned previously, any terrain that nears your river should gradually slope down to a height of 128. Note that you should create rivers after the terrain height field has been created. In order to test your L-system code outside the context of Mini Minecraft, we recommend using the Rasterizer __base code__ (not your Rasterizer implementation) to generate a simple black and white image of a tree made of simple lines, which is formed from the expansion and traversal of your L-system axiom and grammar rules. We highly recommend testing your L-system implementation in this manner, since you'll only have to deal with debugging the L-system functionality, rather than also worrying about how it's being portrayed in the Minecraft environment. You might consider using the following code features for your L-system class implementation: * A `Turtle` class that tracks the position, orientation, and (possibly) "recursion depth" of your drawing turtle. Recall that a turtle is simply a state marker for drawing graphic elements, such as simple lines. * A `std::stack` or `QStack` of `Turtle`s, which will be used to save and restore turtle states as you traverse your grammar string during the drawing phase. * A map from `char` or `QChar` to `std::string` or `QString` that represents what string a given character should expand to during the grammar expansion step. For example, `'X'` might map to `"[-FX]+FX"`, following the example grammar in the procedural graphics slides. * A map from `char` or `QChar` to a function pointer, where the function pointed to performs the drawing operation for the given character. For example, `'F'` might map to a function called `moveAndDrawLine`, which will move the turtle along its orientation vector and draw a line from its start point to its end point. To store a pointer to a void-returning function that takes no inputs, you might find it simpler to `typedef` a "nickname" for a function pointer type:
typedef void (*Rule)(void); // Now we can use Rule in place of the wordy void *(void)

QHash<QChar, Rule> charToDrawingOperation; // Easier for us to read, but still functions as a map from char to "pointer to a function that takes no inputs (i.e. void inputs) and returns void"
During the player's collision detection function, `WATER` and `LAVA` blocks should __not__ cause the player to stop moving. Instead, the player should move at 2/3 its normal speed (both in terms of gravity and lateral movement) and should be able to swim upwards at a constant rate by holding `Spacebar`. Additionally, when the player's `Camera` is inside a `WATER` block, the screen should be tinged blue, while a `LAVA` block should cause a red tinge. You should create these overlay effects by setting up a post-process pipeline like the one in hw04, and render your 3D scene to a frame buffer, then render the frame buffer's texture to a screen-spanning quadrangle with a new shader program. We have created a `FrameBuffer` class to assist you in this process, which you can download [here](framebuffer.zip). __Note that because your team will be using alpha blending this milestone, your post-process fragment shader should output a `vec4` for its color, otherwise you will have uninitialized transparency which will cause rendering errors.__ ### Texturing and Texture Animation ### For this week's OpenGL feature, you'll have to implement several components: * A way to load images as textures into OpenGL for your world. * An addition to `lambert.frag.glsl` that makes use of a `sampler2D` to apply texture colors to a surface. * An alteration to `lambert.frag.glsl` so that it includes a time variable as in Homework 5's OpenGL Fun, and uses this variable to animate the UVs on a `LAVA` block and `WATER` block. You may use a branching statement (i.e. IF statement) in your shader so that only `LAVA` and `WATER` are animated. * Enabling alpha blending in `MyGL::initializeGL` so that transparency can be applied to `WATER` blocks. * Split your interleaved VBO data in the `Chunk` class into two separate VBOs: one for data for all opaque blocks and one for data for all transparent blocks. * An alteration to the VBO for the `Chunk` class, so that it additionally supports UV coordinates (vec2). You may also consider adding some sort of "animateable" flag to your VBO so that only triangles with the `LAVA` and `WATER` textures are animated in your shader. * Additional variables in the `ShaderProgram` class to support these texture features. Using the `BlockType` of a given block, set the UV coordinates of a square face in the `Chunk` VBO so that they correspond to the appropriate texture square in the image below. For example, if you were adding the VBO data for the top of a `GRASS` square, then you'd set the square's UVs so they aligned with the texture area labeled `GRASS TOP` (see image below). If you want to test block appearances, you might consider setting keyboard input that has the Player place a particular block in front of them. Remember that UV coordinates have a range of 0 to 1; the image below is divided into 16x16 block faces, so a single block face ranges from `<x, y>` to `<x+1/16, y+1/16>`. You may wish to refer to the base code provided for homeworks 4 and 5 to see how to read an image file and pass it to your GPU, and associate the texture with a `sampler2D` in a shader. Note that the OpenGL homework base code uses a `.qrc` file to let Qt know that it should include the texture files in its working directory, so that they may be found using the ":/" directory regular expression. When a block is a `LAVA` or `WATER` block, its UVs should be offset in your GLSL shader based on an incrementing time variable. Note that there are rows of `WATER` and `LAVA` face textures in the provided texture image; you can repeatedly move the UVs of a `WATER` block or `LAVA` block across this row to give the illusion of moving fluid across the block surface. In order for transparency to work properly for `WATER` blocks, you will need to add the following two lines of code to `MyGL::initializeGL`: * `glEnable(GL_BLEND);` * `glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);` These tell the OpenGL pipeline that it should use the alpha channel (i.e. the W coordinate) of a fragment's color `vec4` to blend that fragment with the other fragments for its pixel. An alpha of 1 means the fragment is fully opaque, while an alpha of 0 means the fragment is fully transparent. Note that the order in which triangles are rendered will affect how the alpha blending appears, but we aren't going to worry about that for this milestone. Once you've set these flags, you'll need to modify the code that builds a `Chunk`'s VBO so that opaque blocks which are adjacent to transparent blocks have their faces added to the VBO data. This will allow terrain beneath the water to be rendered. The provided texture contains alpha channel information implicitly, so you shouldn't need to add an alpha attribute to your `Chunk` VBOs. As noted in the list above, you'll need two interleaved VBOs in your `Chunk` class. The first will contain all opaque blocks (e.g. `GRASS`, `STONE`, `LAVA`), while the second will contain all transparent blocks (e.g. `WATER`, `ICE`). `MyGL` should render all opaque VBOs first, then all transparent VBOs so that the OpenGL pipeline can correctly sort the rendered triangles according to their alpha (transparency) values. ![](minecraft_textures_all_labeled.png) ### Multithreaded Terrain Generation ### For this milestone feature you will add multithreading functionality to prevent the gameplay from slowing down when new terrain is generated to expand the world. To implement multithreading, you may use any of the multithreading libraries available. However, we recommend either the C++ standard library `std::thread` and `std::mutex` classes, or the Qt library `QRunnable`, `QThreadPool`, and `QMutex` classes. You can download an example program showcasing both these libraries [here](multithreading_file_write.zip) (we will have a lecture exploring this example as well). To begin, you will need to modify how your program's terrain expansion process works. Every `tick`, your terrain should look at every "terrain generation zone" that lies within a radius of two zones away from the player's current position. In other words, every frame your program should examine a 5x5 set of terrain generation zones centered on the zone in which the `Player` currently stands, which is really a 20x20 set of `Chunk`s. For every terrain generation zone in this radius that does __not__ yet exist in `Terrain`'s `m_generatedTerrain`, you will spawn a thread to fill that zone's `Chunk`s with procedural height field `BlockType` data. We will designate these threads as `BlockTypeWorker`s. For every terrain generation zone in this radius that __does__ exist in `m_generatedTerrain`, you will check each `Chunk` it contains and see if it already has VBO data. If it does not, then you will spawn another thread we will designate a `VBOWorker` to compute the interleaved buffer and index buffer data for that `Chunk`. However, `VBOWorker`s will not pass data to the GPU as threads that are not the main thread do not have an OpenGL context from which to communicate with the GPU. We will explain `BlockTypeWorker`s and `VBOWorker`s below. Both worker thread types need to communicate with your main thread (i.e. the thread running your Mini Minecraft executable) through shared memory. For `BlockTypeWorker`s, you will push back filled `Chunk`s into a collection of `Chunk*`s stored in `Terrain`. The kind of collection you use is up to you, but we recommend a `std::unordered_set` or `std::vector`. For `VBOWorker`s, we highly recommend creating a `struct` or `class` to hold four separate `std::vector`s of opaque and transparent vertex and index data for your `Chunk` VBOs, then storing a `std::vector` of these `struct`s in `Terrain`, and add to that collection from each `VBOWorker`. In `Terrain`, every `tick` after attempting to spawn additional threads you should check on the state of these data collections by sending filled `Chunk`s to new `VBOWorkers`, and sending completed VBO data to the GPU. To ensure that `Terrain` and the two worker constructs don't modify their shared memory at the same time, you should make use of mutexes. Designate one mutex for each shared memory structure in `Terrain`. Any time you read from or write to one of these shared memory locations, you should lock the corresponding mutex, and unlock the mutex when you're done with the shared memory. Submission -------- #### Make sure your group commits a readme.txt that explains which group member implemented which feature for this milestone! #### Additionally, in your readme, briefly describe __how__ you implemented your chosen features. Discuss any difficulties you encountered when coding the project, and explain the approach you took to implementing each feature (e.g. "I chose to cast rays from the corners of my player's bounding box for collision detection because I thought it might be an easier approach than overlap checking.") __Your group will present a pre-recorded video of your milestone implementations in class on Monday; you must submit the video to the course dashboard along with your commit link!__ We will grade the code you have pushed to your GitHub repository, so make sure that you have correctly committed all of your files! Once you have pushed your finished project to GitHub, submit a link to your commit through the course dashboard. If you click on the Commits tab of your repository on Github, you will be brought to a list of commits you've made. Simply click on the one you wish for us to grade, then copy and paste the URL of the page into your text file. #### Each group member should submit a commit link to the course Dashboard! ####