{{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. The textures needed for the texturing milestone requirement can be found in the `textures` folder in the root of the repository. 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` * `BEDROCK` 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 Tuesday, April 11 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 caves using 3D noise * Texturing and texture animation in OpenGL * Multithreading of terrain loading ### Cave Systems ### You will implement two main features: * In addition to the 2D height fields used to generate surface terrain, you will add the computation of 3D noise to generate cave systems beneath the terrain's surface. * You will introduce a post-process rendering pipeline into your game engine to render an overlay when the player's head is beneath water and lava. Extend the Perlin noise algorithm discussed in the Noise Functions slides to work in three dimensions; this will require sampling eight surflets rather than four (one for each corner of a 3D grid cell) and using trilinear interpolation on their values. Sample this 3D Perlin noise at every block whose Y coordinate is in the range [1, 128]. Whenever the value of this Perlin noise is negative, the block at the sampled coordinates should be set to `EMPTY`, unless its Y value is less than 25, in which case it should be set to `LAVA`. Finally, all blocks at Y = 0 should be made `BEDROCK`, and made to be unbreakable when left-clicked. The end result will be a system of somewhat-interconnected caves with pools of lava at the lowest depths. If you would like cave openings to be more frequent on the surface of your terrain, then increase the ceiling of Y values at which you sample your Perlin noise to allow carving out the surface height field as well. __Note:__ If you are testing your cave generation without the multithreading portion of milestone 2 working on your local implementation of the project, it is useful to shrink the range of Y coordinates that can contain cave structures (e.g. only Y = [64, 128] or even smaller). Sampling a noise function at every XYZ below Y = 128 can take a long time and will make loading your initial scene very slow on a single-threaded program. 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! ####