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


Overview ----- The overall goal of this project is to create an interactive 3D world exploration and alteration program in the style of the popular computer game Minecraft. This is a group project, with groups consisting of three people. Every week for the next four weeks, each member of your group will complete a milestone feature of the project. Please note that late days cannot be used for the milestones of this project since your partners' features may depend on your own feature implementation. The first two weeks' milestones are set features, but the final week's milestone feature may be entirely of your own choosing; a huge number of possible extensions to this project are possible. Each week, there will be three main features for your group to complete. Each member of your group must choose one of these features to implement, and must implement his or her chosen feature by himself or herself. Each group member will receive a grade for the feature he or she implements; your grade each week is entirely dependent on your work. Since the features of each week affect the features of the following week, you must use version control to collaborate on this project. As with all assignments in this class, you will use Git and Github to track your code. It is a good idea for each member to create a code branch for their chosen feature, so that when they commit their code to the repository it does not cause problems for other group members. By the time a milestone is due, each member should merge his or her branch back into the Master branch so that you have uniform basecode from which to work in the next week. #### Again, please note that late days cannot be used for the final project milestones #### Supplied Code --------- Click here to access the homework's Github repository. 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 1 Milestone Features (75 points each, Due Monday, November 18 at 12:00 NOON) ------- For the first week of Mini Minecraft, each group member will implement a feature that forms the basis of the game engine. On the due date of this milestone, each group will submit a pre-recorded video of their completed work. 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 will talk over your video in class, so there's no need to add title cards or audio. To record your program, you might use [FRAPS](http://www.fraps.com/) if you have a machine running Windows, or use QuickTime's built-in screen recording capability if you're running Mac OS. If you have a Linux machine, we leave finding suitable screen capture software to your no doubt more than capable hands. The three main milestone features are: * Procedural generation of terrain using noise functions. * Altering the provided Terrain system to efficiently store and render blocks. * Constructing a controllable Player class with simple physics. ### Procedural Terrain ### Using a fractal Brownian noise function to generate a height field, fill the provided Terrain construct with blocks. The provided Terrain class contains 64 x 256 x 64 blocks; the Minecraft world is always 256 blocks tall, but is infinitely scalable in the X and Z directions. The terrain you generate should fill the space from Y = 0 to Y = 128 entirely with `STONE` blocks, and when 128 < Y <= 256, the terrain should be `DIRT` blocks at a height dictated by the FBM height field. Each block at the very top of a "column" in your world should be made a `GRASS` block. Play around with different scaling factors for your height field and see what gives you nice-looking results. If you'd like to test your FBM outside the scope of the provided base code, you might consider going back to your homework 5 code and adding a post-process shader that, rather than modifying the image of Wahoo, generates a greyscale image based on the FBM: ![](fbm.png) When the user moves the player avatar near the bounds of the currently generated world (e.g. within four blocks or so), additional terrain should be generated in a new 64 x 256 x 64 volume at the boundary nearest to which the player has moved. This new height field should smoothly blend with the existing terrain height field if you've properly implemented your fractal Brownian noise function. The group member who implements the improved Terrain system (see below) should provide you with some functions that allow you to interface with the Terrain class and add more blocks to it without having to interact with its underlying memory organization. Here is a useful basis noise function that gives you a value between 0 and 1: >
float rand(vec2 n) {
    return (fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453));
}
Additionally, you should allow the user to remove blocks from the world by clicking the left mouse button. To determine which block to remove, cast a ray aligned with your camera's Forward vector and test it for intersection with the blocks near the player. If any of those blocks are hit, remove the one with the smallest `t` value. You may consider using either ray marching or explicit ray-object intersection for determining the block that is to be removed. You should also allow the user to place a block in the world by right-clicking. The block should be placed touching the face of the block that the player is presently looking at, provided it lies within a minimum distance (say, two units away). You may place any kind of block you like when you right-click. For testing purposes, you might consider making a new block type (e.g. `LAVA`) and placing that in the world. You'll have to update the way your local copy of `MyGL` draws terrain if you want the new block to show up. ### Efficient Terrain Rendering and Chunking ### Using the provided OpenGL framework, create a system of VBOs to render your terrain more efficiently. In actual Minecraft, terrain is rendered using a "chunk" system wherein the terrain is divided into 16 x 256 x 16 volumes. We will follow this same system in our Mini Minecraft; to do so, you should add a class called `Chunk` to `terrain.h` with the following members: * A 1D array of `BlockType`s that holds 16 x 256 x 16 blocks, i.e. 65,536 blocks. * A `const` function that takes in an X, Y, and Z relative to the minimum corner of the `Chunk` and returns the `BlockType` located at that position in the `Chunk`. Note that you'll have to convert this 3D coordinate to a 1D index into your array. * A function just like the one described above, but which is _not_ `const` and which returns a reference to the `BlockType` located at the 3D coordinate. Additionally, your `Chunk` class should inherit from the `Drawable` class and implement all of the necessary functions it inherits. Importantly, `Chunk::create()` should only create vertex data for block faces that lie on the boundary between an `EMPTY` block and a filled block. In other words, don't create VBO data for 65,536 cubes (equivalent to 786,432 triangles!) if a `Chunk` comprises entirely of `DIRT` blocks. Rather, you would create VBO data for the hull of the `Chunk`, which comprises only (16 x 16 x 2) + (16 x 256 x 4) = 16,896 squares (equivalent to 33,792 triangles). To be even more efficient, if a `Chunk` is adjacent to another `Chunk`, every block in the first `Chunk` should check if it's adjacent to a filled block in the other `Chunk` when building its vertex data, and only create a square if the adjacent block is `EMPTY`. You should modify the `Drawable` class so that it uses a single interleaved vertex buffer object to store the position, normal, and color data of an object. This also means modifying `ShaderProgram::draw` to handle interleaved VBOs. For example, if a VBO were ordered `pos1nor1col1pos2nor2col2` then the `glVertexAttribPointer` associated with `vs_Pos` (a `vec4`) would look like `glVertexAttribPointer(attrPos, 4, GL_FLOAT, false, 2 * sizeof(glm::vec4), (void*)0)`. Likewise, the `glVertexAttribPointer` call for setting up `vs_Nor` would look like `glVertexAttribPointer(attrPos, 4, GL_FLOAT, false, 2 * sizeof(glm::vec4), (void*)sizeof(glm::vec4))`. Now that you have a `Chunk` class, instead of storing a 3D array of `BlockType`s in your `Terrain`, you will store `Chunk`s instead. However, each `Chunk` needs a location in the world, since an individual `Chunk` only knows its blocks relative to its local origin (its lower-left-back corner). To achieve this, you will create a map (e.g. `std::unordered_map` or `QHash`) of (X, Z) coordinate to `Chunk`, where the coordinate key represents the location of a `Chunk`'s local origin in world space. Note that since `Chunk`s span the entire height of the world, there's no need to track a Y coordinate for their origin (it'll always be 0). Since most standard "pair" classes like `glm::ivec2` or `std::tuple` are not inherently hashable, you will either have to implement a hash function of your own for your chosen map key class, or be tricky with integers: if you use a 64-bit integer (i.e. `int64_t` or `uint64_t`), you can store a 32-bit X coordinate in its upper 32 bits, and a 32-bit Z coordinate in its lower 32 bits. It's up to you how to approach this, but we think the 64-bit integer is a little easier (and kind of cooler). ### Game Engine Update Function and Player Physics ### In Mini Minecraft, we will use the `MyGL` class as our "game engine" construct, meaning `MyGL` will eventually need to know about all entities of which the game comprises and will need to handle processing each entity each time the game updates. To begin, you should create a `Player` class with the following properties at minimum: * A position in 3D space * A velocity in 3D space * A pointer to a `Camera` * A set of variables to track the relevant inputs from the mouse and keyboard. The `Player` should know whether or not the `W`, `A`, `S`, `D`, and `Spacebar` keys are held down. The `Player` should also track the change in the cursor's X and Y coordinates as well as the state of its left and right mouse buttons. * A function that takes in a `QKeyEvent` from `MyGL`'s key press and key release event functions, and updates the relevant member variables based on the event. * A function that takes in a `QMouseEvent` from `MyGL`'s mouse move, mouse press, and mouse release event functions, and updates the relevant member variables based on the event. In order to more easily showcase your group's Minecraft world, you should enable the character to fly by pressing the F key on the keyboard. This makes the character no longer subject to gravity, and be able to move upwards by pressing E and downwards by pressing Q. Furthermore, the character should no longer be subject to terrain collisions. Note that to handle the `Player's` "event listener" functions, you should simply invoke them from `MyGL`'s various key and mouse event functions, and pass them the relevant event. Unlike the base code setup, your `MyGL` should not directly modify the `Player` or `Camera` from within its key or mouse event functions. Next, you should modify `MyGL::timerUpdate` so that it performs the following functions in order: 1. Compute the time elapsed since the last update call. This is not as simple as one might think since, while the timer linked to `timerUpdate` is supposed to fire every 16 milliseconds, it is not _guaranteed_ to do so. As such, you'll need to manually compute the change in time. To do so, we recommend the use of Qt's `QDateTime::currentMSecsSinceEpoch()` function, which returns the number of milliseconds that have elapsed since January 1, 1970 at 00:00:00 UCT as a 64-bit integer (`int64_t`). If you obtain this value each timer update and store it, it is simple to compute the actual change in time. You'll use this change in time for things like player physics and (in future weeks) shader animation. 2. Iterate over all entities that are capable of receiving input from your "controller" (in this case, the mouse and keyboard) and read their present controller state. Since the only entity capable of receiving of input is your `Player`, this is fairly straightforward. Based on the controller state, update the relevant attributes of the entity. For the `Player`, this means modifying its velocity and `Camera` attributes. When `WASD` are pressed, the `Player` should adjust its velocity so that it moves along its `Camera`'s forward or right vectors in a positive or negative direction. When `Spacebar` is pressed, the `Player` should jump (i.e. set its upward velocity to a positive value). When the cursor's X position changes, the camera should rotate about the world up vector, i.e. the Y axis. When the cursor's Y position changes, the camera should rotate about its local right vector. 3. Iterate over all entities in your scene and perform a "physics update" on them. A physics update simply means processing the physics variables of an entity (position, velocity, acceleration, angular velocity, etc.) and updating all dependent variables according to the change in time. For now, the only entity capable of a "physics update" is your `Player`. At all times, the `Player` should be subject to gravity, meaning their vertical velocity constantly decreases. Otherwise, it is up to you how you want to handle the player's physics. 4. Prevent physics entities from colliding with other physics entities. Since we're dealing with the Minecraft world, this is fairly straightforward. If you treat your `Player` as a 1 x 2 x 1 stack of blocks that is always axis-aligned (though not necessarily grid-aligned positionally), it is straightforward to determine if the player is intersecting (or will intersect) a block or blocks of the terrain. Either prevent the `Player` from moving into blocks during the physics update, or move them back out of the terrain after the update has occurred. Note that you may have a slightly less complicated time handling collisions if you give the `Player` a "grounded" versus "aerial" state, such that gravity does not affect them if they are standing on the ground. This does necessitate checking if the `Player` should begin falling in each frame, though, as the player may become airborne after, say, walking off a cliff. 5. Process all renderable entities and draw them. Since `MyGL` itself renders objects, this really means invoking `MyGL::update()` and letting `paintGL` do its work. 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 score will be partially based on a pre-recorded video of your milestone implementations; you must submit a link to 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! ####