Assignment 3: Creating a Virtual World
- Due Feb 24, 2020 by 11:59pm
- Points 10
- Submitting a file upload
Objectives
To create a virtual world using textured cubes and explore it using a perspective camera.
Description
Create a first-person exploration application. A first-person camera (the player) should start in a given position of a 32x32x4 tile-based 3D world (which is made out of textured cubes). The user should be able to navigate in this world using the keyboard and/or mouse to move and rotate the camera. Your application is required to have the following features:
- The world is fully created when the application starts.
- Ground is created with a large plane (square), or the top of a flattened (scaled) cube.
- Walls are created with cubes.
- Walls can have different heights in units (1 cube, 2 cubes, 3 cubes and 4 cubes ).
- The faces of the cube should be textured to make it look like a wall.
- The sky is created with a very large blue cube that is placed at the center of the world
- The world layout should be specified using a hardcoded javascript 2D array.
- Each element of the array represents the height of the wall (0, 1, 2, 3 or 4) that will be placed at that location.
- Camera can be moved using the keyboard.
- W moves camera forward.
- A moves camera to the left.
- S moves camera backwards
- D moves camera to the right.
- Q turn camera left
- E turn camera right
Extras for an A
- Camera can be rotated with the mouse.
- Add multiple textures to make your world more interesting
- Make your blocky animal run around the world
Hints about which order to do this assignment
First off, which code should you use as a starter? If you got your animal working or at least drawing a couple cubes, I think you should start with that and you can add texture and camera to that. I think it will be easier than either starting from the texture example and expanding it to a world or starting with the camera example and expanding it to texture.
Next, which order should I do this? Texture first (chap 5), or camera first (chap 7), or world first? My description below will just follow the book order, but I think you can do the camera first if you prefer with no special problems. The world building will be easier if you have a working camera already.
Texture (Chap 5)
- Make sure you can draw a cube. If you are starting from your animal code, then great, you've got multiple cubes already. If you used the drawElements method from the book, make sure you have different vertices for each face (not sharing a vertex between faces). That is, figure 7.36, not 7.33 in the book. If you used drawArrays with gl.TRIANGLES as I suggested, then you are all set.
- You are going to need to send UV coordinates (the book calls them ST coordinates) to handle texture. This is a second attribute variable in GLSL. You need to decide whether you want to use two buffers (pg 137-140) or interleave a single buffer (pg 141-145). Either is ok, but step 2 is making sure you can get 2 attributes working. I suggest using color as the second attribute to debug this step. You should be able to give each vertex a different color and get rainbow looking cubes. Don't skip this. Its hard to debug UV passing but easier to debug color passing since you can directly see what is happening.
- Local web server - Before we can load a texture file, we are going to need a webserver. You can't just click the .html file anymore because of security issues. The book gives a way to bypass security and just use the browser, but you shouldnt bypass security. This is a fine time to learn to run a local web server on your machine. Do that. Make sure you can view your webpage through this local server.
- Get texturing to work - Either switch your color attribute to UV, or add a third attribute. Make sure you have the UV coordinates on the vertices of your cube. Copy the relevant parts of setting up a texture from the book example to your code. Modify your fragment shader to look up texture instead of using a color.
- You need a blue sky but textured walls. Thus - Get color and texture working at the same time. You could learn how to use different shaders on different objects, but I recommend you just use a single shader for all of the objects, since its much simpler. Create a new uniform variable to indicate if this object is supposed to use color or texture and then use an 'if' statement in your shader to execute different things for different objects. The same uniform variable could indicate which texture to use if you have multiple different textures.
- Success! You have a textured cube!
Camera (Chap 7)
- Change your vertex shader to glPosition = u_ProjectionMatrix * u_ViewMatrix * u_ModelMatrix * a_position; The book has lots of examples that have some matrices, but not others. I believe this project is conceptually easiest if you pass all three as uniforms to the vertex shader and multiply them there. Pg 263 has a shader set up the way I am talking about. Just set Projection and View to the identity matrix for now. If you previously had u_GlobalRotationMatrix, that one is going to become the new u_ViewMatrix. You could leave it attached to your slider from assignment 2 until you are ready to use LookAt.
- Use LookAt to set the camera position and viewing direction. This matrix is the ViewMatrix. Test that this is working by just changing the numbers in LookAt directly in the code and verifying that the camera behaves as you think it should. For instance, move forward a little, or back a little. LookAt (eye=(0,0,0), at-(0,0,-1), up=(0,1,0)) should produce an identity matrix I think, so its a fine starting point for LookAt(). The start of chap 7 discusses LookAt().
- Set up some global variables to keep the eye, at, up vectors. Change your code to use these variables in your drawScene() routine.
- Get the keyboard working. When ever someone hits W, modify the global variables EYE and AT appropriately to move forward. You need to figure out the direction to move as DIR=AT-EYE; Then update AT=AT+DIR; EYE=EYE+DIR; You dont need to modify UP. The onkeydown() method is in the section starting pg 238. Test that this works.
- Add more keys for moving back, left, right. Turning is a bit more complicated. You are on the 2D ground plane currently pointed in some direction theta. You want your new direction to be something like theta+5 degrees. You should use trigonometry to convert your vector direction to an angle, modify your angle, and then convert back to a vector direction, and finally update your AT point.
- You are ready for u_Perspective. Just use the setPerspective() command to get a matrix and pass it as a uniform. If you aren't providing control of field-of-view (which isnt required but lots of people like to add), then you can do this once and never change it again. Discussion of perspective starts pg 254.
World Building
- Make sure you have your camera working already with keyboard control. If not, go do that section first. You can't debug your world if you cant actually see all of it.
- In the same way you built an animal by placing cubes, you can build a world placing cubes. However this will genuinely suck to code by hand if you need 100s of cubes to specify your world. Instead we will build it programatically. Define a small 2D array = [[ 1 0 0 1] [1 1 0 1] [ 1 0 0 1] [ 1 1 1 1 ]]. Now write a double loop and place a block whereever there is a 1. --- psuedocode --- for width=1:4 {for height=1:4 {if map[width][height]==1 {ModelMatrix.setTranslate(width,height); DrawCube()}}}
- Make sure the simple 4x4 map is working above. You should be able to walk around this tiny world. Update your starting EYE location to make sense.
- Expand your map to 32x32. Instead of just making a 1 unit wall, make the wall N units high according to what you put in the 2d map array. You don't need to specify your map exactly like this if you want to be more fancy. Making just walls wont allow your world to have ceilings. You could for example make a 3D array map, or you could use a 2D map with the stored number bit values keeping the levels to place blocks on, e.g. if you stored 33=00100001 then you would have blocks on the first and 6th levels, but no blocks on the others. You just need to end up with a world that is at least 32x32 with blocks on more than one level that you specify in variables and build programatically. You can't just write 100 calls to drawCube() in your code. You should be able to 'edit the map'.
- Thats it! You have a world!
But I wanted an A!
- You could do these in any order that seems easiest.
- Camera can be rotated with the mouse. - Most games have this control. You need to add an onMove() function and map this to the rotation you previously had on QE keys. Its a little tricky since with keys you can just turn 5degrees, but with mouse you have to keep track of the old mouse location and figure out an appropriate amount to turn based on the new mouse location.
- Add multiple textures to make your world more interesting - The book talks about multiple textures on pg 183. The easiest way to handle multiple textures is to just set up the samplers and texture units for all the textures and then just leave them (you dont need to pass all the textures with each drawArrays(), they will stay there). Just use a uniform variable to specify which texture to use and update this uniform before each call to drawCube(). Now different cubes can have different textures. You'll have to modify your map to say which kind of cube to draw as well. You may want to update your sky to have a different texture and ground as well. You can have up to 8 textures at a time loaded in webGL.
- Make your blocky animal run around the world - If you started from your blocky animal, it may still be there, without you having to do anything. The reason this is an "extra for A", is that you have to keep the tick animation working while you are moving the camera around your world. Depending on your code structure, this could be a hassle. And you should do something to keep your animal from going through walls. If you design your world with a big empty space, they could just keep the creature in that space. Or you could do very basic collision detection using your map. Add some variables for 'creature location' and then let the creature do a random walk around your world. Check for collisions and if the creature location is about to go where the map says there is a block, just randomly change direction and walk somewhere else.
FAQ
- I'm not done with the basic stuff, but I already did an 'Extra for A'. Is that ok? - Yep. Its all just points and you'll still get them, but I think its harder to get the A points than the others, so I recommend starting with the basics.
- I want to have this amazing castle in my world, will I lose points for not just having blocks? - Its usually fine to do extra. Just stick your castle in your block world and then obviously you completed the assignment.
- But I wanted a terrain map instead of a blocky world. Can I? This is fine too. Make your map variable encode the height of the ground, and create triangles to make the ground. Put a texture on the ground covering all the triangles. As long as you have a texture, and you have a map that is specifying a world you can walk around in, then you're good. I just think the blocky world is easier for most people since you already have drawCube().
- But I want to load my world from a file. Hard coding is stupid! - Yep. Hard coding is stupid. But its much easier. Load from a file if you want to do that. Then you can even load different 'game levels' from different files.
Resources
- Readings:
- (WebGL) Matsuda/Lea Ch5 (About texture)
- (WebGL) Matsuda/Lea Ch7 (About cameras and keyboard events (you dont need pg 276-289 about drawElements())
- (WebGL) Camera Movement Tutorial: http://learnwebgl.brown37.net/07_cameras/camera_movement.html Links to an external site.
- (HTML/Javascript) How to handle keyboard events: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent Links to an external site.
What to Turn in
1. Canvas Submission
Zip your entire project and submit it to Canvas under the appropriate assignment. Name your zip file "[FirstName]_[LastName]_Assignment_4.zip" (e.g. "Lucas_Ferreira_Assignment_4.zip").
2. Live Hosted Submission
Host your assignment on your UCSC portal. This can be achieved by just putting your entire project directory into the 'public_html' folder which you can access via the UCSC Unix timeshare. Your site link is 'https://people.ucsc.edu/~ucsc_user_name/'.
WHEN SUBMITTING YOUR PROJECT ON CANVAS, PLACE YOUR SITE LINK AS A COMMENT OF THE SUBMISSION.
Read the SubmissionGuide.txt file for further explanation on how to submit your assignment.
Rubric
Criteria | Ratings | Pts |
---|---|---|
Have a ground created with a plain of flattened cube.
When the application is opened, one should see a ground .
threshold:
pts
|
pts
--
|
|
Have walls created with textured cubes.
When the application is opened, one should see a world with walls with different heights (in units 0, 1, 2, 3 or 4). Built from map variable. (Or a terrain if you wanted to do that)
threshold:
pts
|
pts
--
|
|
Implement camera rotation.
Using the Q/E keys to rotate the view left/right (pan) without moving the camera position.
threshold:
pts
|
pts
--
|
|
Implement camera movement.
W - move the camera forward;
A - move the camera to the left; S - move the camera backwards; D - move the camera to the right;
threshold:
pts
|
pts
--
|
|
Texture working on at least one object
threshold:
pts
|
pts
--
|
|
Perspective camera implemented.
threshold:
pts
|
pts
--
|
|
Have a sky box created with a large blue cube.
When the application is opened, one should see a sky in the Horizon.
threshold:
pts
|
pts
--
|
|
Extra for A - Mouse control of turning
threshold:
pts
|
pts
--
|
|
Extra for A - Multiple textures
At least two different textures appear in your world
threshold:
pts
|
pts
--
|
|
Extra for A - Block animal
You have your animal(s) running around your world.
threshold:
pts
|
pts
--
|