1
Sage Demo 1Terrain, Water, Console
SAGE Lecture NotesIan Parberry
University of North Texas
2
3
4
Sage Demo 1• Demo 1 has four functions
– Getting started writing a game– Creating terrain– Creating water– Creating a console
5
Key Topics• Play Controls• Changes from Demo 0• New and Changed Files• Water• Terrain• Console
6
Play Controls• Input
Escape Exit demo
~ Toggle console
Left Arrow Strafe left
Right Arrow Strafe right
Down Arrow Move backwards
Forward Arrow
Move forward
Page Up Move upward
Page Down Move downward
Mouse Look around
Table 1: Game Controls
7
Play Controls Cont.• Console Commands
Command Function
Help string Provides a description of any given command or displays a list of all commands. Pass “–a” as the parameter to see a list of all commands.
Problems boolean Outputs any errors linking command comments from an XML.
Wireframe boolean Enables/Disables wireframe rendering
Fog boolean Enables/Disables fog
Cameraspeed float Sets the speed of the free camera in world units per second.
Info boolean Enables/Disables FPS (frames per second) in upper left hand corner
Ambient int int int Sets the ambient lighting in the scene. Integer parameters should range from 0 to 255. Parameters one, two, and three represent color components red, green, and blue respectively.
Dircolor int int int Sets the directional light color in the scene. Integer parameters should range from 0 to 255. Parameters one, two, and three represent color components red, green, and blue respectively.
8
Play Controls Cont.• Console Commands Cont.
Lightdirection float float float
Sets the direction of the directional light. The direction will be normalized automatically. Float parameters one, two, and three represent light vector coordinates x, y, and z respectively.
Terraindistort boolean Enables/Disables the distortion of texture coordinates on the terrain.
Lod int Sets the level of detail of the terrain. The parameter should range from 0 - 2 with 0 being the highest detail. Any number out of range specifies distance base level of detail.
Terraintexture string Sets terrain texture to a specified texture.
Terrainscale float Sets the scaling factor of the texture on the terrain.
Crackrepair bool Enables/Disables crack repair between the terrain sub-meshes
9
Incremental Changes• Models – S3D Model Loading• Terrain – New Class• Water – New Class• Console – New Class• Derived Game Class - Rewritten
10
New and Changed Files• Ned3D/Source
– Game.h / Game.cpp• Sage/Source/Terrain
– Terain.h / Terrain.cpp– HeightMap.h / HeightMap.cpp– TerrainSubMesh.h / TerrainSubMesh.cpp
• Sage/Source/Water– Water.h / Water.cpp
11
New and Changed Files• Sage/Source/Console
– Console.h / Console.cpp– ConsoleCommands.cpp– ConsoleCommentEntry.h /
ConsoleCommentEntry.cpp– ConsoleDefines.h– ConsoleFunctionEntry.h / ConsoleFunctionEntry.cpp– ParameterList.h– TextParser.h / TextParser.cpp
12
Water
13
Water Design• Rendering the Mesh
– Rendered as a plane partially above the terrain• Texture Translation
– Once per frame the texture coordinates are changed to give the illusions that the water is moving
• Transparency– The water will be partially transparent, so it will have
an alpha value of less then 1.0, but greater than 0.0 (otherwise, you couldn’t see it)
14
Water Class Overview• Water() – Constructor
– Initializes the water class• process()
– Update the water’s texture and geometry• render()
– Draws the water to the screen
15
Water::Water()• Allocates vertex and index buffers• Sets up the index buffer
// create vertex and index buffers m_vertexBuffer = new LitVertexBuffer(numVerts, true); m_indexBuffer = new IndexBuffer(2 * numQuads); // Fill in index buffer if (m_indexBuffer->lock()) { //set triangles for (int i = 0 ; i < numVertsPerSide-1 ; i++) { for (int j = 0 ; j < numVertsPerSide-1 ; j++) { (*m_indexBuffer)[i*(numVertsPerSide-1) + j].index[0] = i*numVertsPerSide+j; (*m_indexBuffer)[i*(numVertsPerSide-1) + j].index[1] = (i+1)*numVertsPerSide + j + 1; (*m_indexBuffer)[i*(numVertsPerSide-1) + j].index[2] = (i+1)*numVertsPerSide + j; (*m_indexBuffer)[numQuads+i*(numVertsPerSide-1)+j].index[0] = i*numVertsPerSide + j; (*m_indexBuffer)[numQuads+i*(numVertsPerSide-1)+j].index[1] = i*numVertsPerSide + j + 1; (*m_indexBuffer)[numQuads+i*(numVertsPerSide-1)+j].index[2] = (i+1)*numVertsPerSide + j + 1; } } m_indexBuffer->unlock(); // unlock }
16
Water::process()• Maintains Drift Animation – see Water.cpp
Lock VertexBufferfor z = 0 to vertsPerSide - 1 for x = 0 to vertsPerSide - 1 { Vertex.position = (x,0.0f,z) Vertex.texCoords = (x,z) Vertex.color = ARGB(128,255,255,255) VertexBuffer[x + z * vertsPerSide] = Vertex }Unlock VertexBuffer
Figure 7: Filling the Vertex-Buffer
17
Water::render()• Alpha Blending
– setBlendEnable() – Enables/Disables alpha blending (transparency)
• Texturing– selectTexture() – Specifies what texture to
use when drawing the water• Lighting
– setLightEnable() – Enables/Disables lighting on the water
18
Possible Improvements• The following things are a few possible
improvements you may consider adding to the water class– Reflections– Ripples– Bump Mapping– Waves
• In Demo 2 we will add some of these using pixel and vertex shaders
19
Terrain
20
Terrain Design Overview• The terrain design is composed of 4 parts
– Terrain Class– Height Map– Level Of Detail (LOD)– Submeshes (for LOD)
21
Level of Detail (LOD)• LOD is a way of decreasing the number of
polygons that need to be drawn for the terrain by using fewer triangles to draw items that are farther away from the camera.
• Replace 8 small triangles with 2 large triangles
• Repeat the texture as necessary
22
LOD Cont.• Layout
• Breakdown
LOD 0: 32 Triangles LOD 1: 8 Triangles LOD 2: 2 Triangles
Figure 13: Rendering at different LODs
Figure 12: Level of Detail Grid
23
LOD cont.• The issue with using an LOD terrain
algorithm is that it creates cracks in the terrain where the level of detail drops off
24
LOD Cont.• Close up view of a crack in the terrain
caused by the LOD
25
LOD Cont.• Solution
– Find a crack– Move the points on the higher resolution
section of the terrain to match the triangle on the lower resolution section
26
LOD Cont.• Finished Product
27
Height Maps
28
Height Maps• A height map is a simple way to represent
a terrain that uses an a 2D array of height values
for (int y = 0 ;y < m_nSide - 1; y++){ for (int x = 0 ;x < m_nSide - 1; x++) { m_fHeight[y][x] = ((float)(0x000000FF &
bitmap.getPix(x,y)) / 256.0f) * maxHeight; }}
Figure 22: Filling the height array
29
SubMeshes• Breaks up the terrain into smaller meshes
to use with the LOD• Each section has an LOD associated with
it that reduces its number of triangles based on what LOD the subMesh is at.
• Constructor builds a flat layout of the subMesh with the specified LOD
30
SubMeshes Cont.• setMesh() – Fills the subMesh with vertex
data• render() – Checks for cracks, then renders
the subMeshconst unsigned int LODCRACK_TOP = 0x04;const unsigned int LODCRACK_RIGHT = 0x8;const unsigned int LODCRACK_BOTTOM = 0x10;const unsigned int LODCRACK_LEFT = 0x20;const unsigned int LODCRACKPRESENT = 0x3C;
if (lodcrack & LODCRACKPRESENT){ ...... Code that fills in the crack .......}
Figure 23: LOD crack flags
Figure 24: Checking for a crack
31
Terrain Class• Brings the HeightMap and SubMesh
classes together with the LOD system to build the terrain– The height map is split into several sub
meshes based on their distance from the camera
– The sub meshes actually do the rendering, the Terrain class just tells them to render
32
Terrain Class Cont.• Terrain::Terrain() – The terrain constructor
performs the following tasks– Loads a height map from a file– Creates a vertex array– Calculates the terrains normal vectors– Calculates the terrains position– Calculates the terrains texture coordinates
33
Terrain::Terrain() Cont.• Loading the height map
– The height map is created from an image file passed into the constructor by the HeightMap class
– Terrain sub meshes are initialized with dimensions of the height map
• Calculate the optimal level of detailfloat minTrianglesPerRender = 128;float triPerSubmesh = (float)m_nSubmeshSide * m_nSubmeshSide * 2; float result = log(triPerSubmesh/minTrianglesPerRender) / log(4.0f);m_nMaxLOD = (int)(result + 0.5f) + 1;
Figure 26: Calculating optimal levels of resolution
34
Terrain::Terrain() Cont.• Calculating the normals for the terrain
– The normal of the vertex in the terrain is calculated by averaging the normals of the connected triangles
• SubMesh Data– Two dimensional array implemented using a
triple pointer that represents the subMeshes and their respective LODs
TerrainSubmesh*** m_pSubmesh; // Stores all TerrainSubmesh objects
Figure 27: m_pSubmesh delaration as a TerrainSubmesh triple pointer
35
Terrain::Terrain() Cont.• SubMesh Data Cont.
– The first dimension or the SubMesh LOD array represents the level of detail and the second dimension represents every sub-section.
• In other words, we could get a pointer to the third sub-section with an LOD of 2 with the statement seen in Figure 28.
m_pSubmesh[2][3]; // gets third sub-section TerrainSubmesh object at LOD 2
Figure 28: Example of getting a pointer to the 3 rd sub-mesh of level of detail 2
36
Terrain::Terrain() Cont.• SubMesh Data Cont.
– The last thing is to create a two dimensional array of unsigned integers to help use with the LOD rendering process.
• This array provides one unsigned integer for each sub-section holding the level of detail that the sub-section should be rendered at and information about how to render that sub-section to avoid cracks.
37
Terrain::setCameraPos• Calculates the LOD for all submeshes based on the given camera
position• Calculates where the cracks will be located and stores them in an
array for future use– Figure 29 shows how the first 2 bits represent the level of detail
and the next 4 bits represent the presents of cracks along a each side. The example in Figure 29 shows a sub-section with a LOD of 2 with cracks along its right and left sides.
Level of DetailCrack Locations
Figure 29: How the TerrainSubmesh render information is stored
38
Terrain::render()• Checks flags to see if its render states
need updating– These flags are modified by the console
• Renders all the submeshes of the terrain// Render all the partsfor (int i = 0 ; i < m_partCount ; ++i){ gRenderer.selectTexture(m_partTextureList[index]);
// Render the part gRenderer.render( m_vertexBuffer, 0, m_partMeshList[index].getVertexCount(), m_indexBuffer, m_indexOffsets[index], m_partMeshList[index].getTriCount());}
39
Console
40
Console• A command prompt available at run time
to execute specific commands.• Helps with the debugging process
41
Console Functions• The following are a few functions that
could be implemented in a console– Turn On/Off wireframe mode– Turn On/Off FPS (frames per second) display– Turn On/Off Lighting– Add objects to the scene– Change an object’s position
42
Console Benefits• Programming without a console results in
a large number of key bindings that can get confusing
• Changing the way graphics are displayed on the fly make it easier to find problems
43
Console Design Overview• Display – The user must be able to see
the console and text on the screen• Adding Functions – The developer must
be able to easily add custom commands• Calling Functions – The user must be able
to type commands into the console to execute these functions
• Help Commands – The user must be able to get information about a command
44
Display• The console consists of the following display
elements– A background image
• This makes the console and text easier to see
– A multi-line text display that shows previous commands and their output
– A text prompt that takes user input• The tilde key (‘~’) displays/hides the console• After typing in a command, the enter key sends
the command to the console to be processed
45
Adding functions• A console function consists has four parts
to it– The command name– The parameters’ type– The parameters– A pointer to the function that it calls
46
Calling Functions• After a command is entered into the
console, the console system must perform a few operations– A command lookup based on the name of the
command– A type check on each of the parameters
• This keeps the console from having negative side effects related to the user supplying the wrong data for the command
47
Help Command• Allows the user to see a list of available
commands– This can be done in the SAGE console by typing ‘help
–a’• Also give help on a specific command
– This can be done in the SAGE console by typing ‘help command_name’
– Gives a brief description of the command as well as a list of its parameters and their types
– A commands help contents must be specified in the console systems xml file
48
Console Implementation Overview
• The following utility functions needed to be created to help in the development of a console for SAGE– ParameterList – Stores parameters as they
are passed to custom functions– TextParser – Controls parsing text into
parameters type and values
49
ParameterList Structure• Stores parameter values so we can pass
them into custom functionsstruct ParameterList{ std::string Strings[MAX_PARAMETERS]; ///< holds string parameters int Ints[MAX_PARAMETERS]; ///< holds integer parameters float Floats[MAX_PARAMETERS]; ///< holds float parameters bool Bools[MAX_PARAMETERS]; ///< holds boolean parameters Vector3 Vector3s[MAX_PARAMETERS]; ///< holds Vector3 parameters int numStrings; ///< number of string parameters available int numInts; ///< number of integer parameters available int numFloats; ///< number of float parameters available int numBools; ///< number of boolean parameters available int numVector3s; ///< number of vector3 parameters available
int numParameters; ///< number of parameters available};
50
TextParser Class• Converts strings into specific parameter
types– This converted data is passed into the
console functions through the ParameterList structure
51
Building the Console Class• Console Class Function Overview
– initiate() – Allocates resources– process() – Controls moving the console– render() – Draws the console– pressedChar() – Handles key press messages and
passes them into the console– addFunction() – Adds a custom function to the console– processLine() – Parses and processes a line of text– loadCommentsFromXML() – Loads command
descriptions from an XML– helpCommand() – Displays information about a
console command
52
Console::initiate()• Allocates background texture and font,
and loads xml information// cache the background texture to use from the engine resources // directorygDirectoryManager.setDirectory(eDirectoryEngine);m_textureHandle = gRenderer.cacheTexture(m_textureName.c_str(),false);
// create a render target m_renderTargetHandle = gRenderer.allocTexture(
"consoleTarget",m_renderTargetWidth,m_renderTargetHeight, true);
// load the fontm_fontHandle = gRenderer.addFont("Arial", m_textWidth, m_textHeight, true);
// load command comments from engine resources directorygDirectoryManager.setDirectory(eDirectoryEngine);loadCommentsFromXml("consoleDoc.xml",false);
Figure 32: Most important parts of Console::initiate
53
Console::process()• Animates the console as it slides onto and
off of the screen from the topif (m_consoleActivating){ if (timePassed >= m_timeForTransition) { m_consoleActivating = false; m_consoleCurrentLocation = m_finishCenterPoint; } else // the console is still animating { ratio = timePassed / m_timeForTransition;
// interpolate between start and end m_consoleCurrentLocation = (1.0f - ratio) * m_startCenterPoint + ratio * m_finishCenterPoint; }}
54
Console::render()• Renders the console to the render target
texture created in initiate– We render to a texture because it is easier
then drawing directly to the screen because we don’t have to worry about where the console is on the screen
– This allows for the application of some extra special effects on the console
55
Console::render() Cont.