4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project...

31
4D3 Computer Graphics OpenGL Project Report Mikhail Volkov <volkovm at tcd dot ie> 04400593 January 18, 2010 Department of Electronic & Electrical Engineering Trinity College Dublin

Transcript of 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project...

Page 1: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

4D3 Computer Graphics

OpenGL Project Report

Mikhail Volkov<volkovm at tcd dot ie>

04400593

January 18, 2010

Department of Electronic & Electrical Engineering

Trinity College Dublin

Page 2: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

Contents

I User Documentation 3

1 Introduction 3

2 Getting Started 42.1 Operating System Requirements . . . . . . . . . . . . . . . . . . . . . . 42.2 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.3 Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

II Developer Documentation 5

1 Technical Overview 51.1 Why C#? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.2 Why Tao? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.3 Why FreeGlut? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2 Implementation Details 62.1 Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.1.1 Mouse Look . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.1.2 WASD Movement . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2 Representation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.3 Physics and Collision Detection . . . . . . . . . . . . . . . . . . . . . . . 13

2.3.1 Collisions between Entities and Bullets . . . . . . . . . . . . . . . 132.3.2 Collision with Walls . . . . . . . . . . . . . . . . . . . . . . . . . 142.3.3 Bullet Management . . . . . . . . . . . . . . . . . . . . . . . . . 16

2.4 Models and Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172.4.1 Hierarchical Spider Models . . . . . . . . . . . . . . . . . . . . . 172.4.2 Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.4.3 Animated Sky . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

2.5 Gameplay and AI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252.5.1 Spider Bite Handling . . . . . . . . . . . . . . . . . . . . . . . . . 252.5.2 Spider AI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252.5.3 Spider Eggs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

2.6 Stereo Display Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

3 Sources 31

4 Final Remarks 31

Page 3: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

Part I

User Documentation

1 Introduction

Beware of the Spider was meant to be an introspective exploration of my irrationalfear of spiders. Armed with a flak cannon, the protagonist navigates through a maze,slaughtering giant arachnids and leaving a trail of blood and carnage in his wake. Theobjective of the game is to find and destroy the (obviously evil) Gargantuan SpiderQueen. But it is no easy feat! The labyrinth is infested with her progeny and the floorsare littered with spider eggs.

You start with 100 life and 200 shells of ammunition. If a spider sees you he willchase until he catches you, or until you are out of sight. If a spider catches you he willcommence feasting on your blood and you will continuously lose life. Should your lifereach zero, you will be dead. Finally, you should tread carefully around spider eggs oryou may be in for a nasty surprise. You have been warned!

The game features a first-person view, WASD navigation with mouse look, collisiondetection with the environment and between entities, animated hierarchical spiders,enemy AI, and last but not least, in the true spirit of cheesy horror movies from thesixties, the game features an old school red-cyan anaglyph mode.

Figure 1: Startled people watching a 3D movie

3

Page 4: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 GETTING STARTED

2 Getting Started

2.1 Operating System Requirements

The game uses the resolution of your primary monitor and runs in fullscreen. It wasdeveloped and tested on Windows Vista (32-bit), and it runs very smoothly at 1280×800and 1280×1024 resolutions.

It does not run well on Windows XP! The mouse and keyboard sensitivities do notagree between the two operating systems, and it is generally slow for some unknownreason. Avoid running it on XP, as it will ruin the game.

2.2 Setup

Beware of the Spider requires that you have the latest FreeGlut binaries (available athttp://freeglut.sourceforge.net) and the latest version of the Tao Frame-work (available at http://sourceforge.net/projects/taoframework) installedon your computer.

Once everything is set up, you can run the .exe or open the C# solution and runusing Visual Studio. You must run the .exe from its original directory as it will lookfor resources in specific locations.

2.3 Controls

The basic controls allow you to navigate through the maze and to shoot. This is allyou need to play the game. With the advanced controls you can enable stereo modeand change the stereo settings. The default stereo settings are optimized for mostpeople on most display monitors and you are advised not to change them. However, noteverybody perceives stereo in the same way, and you can fine-tune the stereo settingsusing the arrow keys if you really want. The focal length determines the point of thescene which is on the projection plane. In other words, everything in front of that pointwill be perceived as coming out of the screen and everything behind that point will beperceived as going into the screen. The interocular distance is the distance between thetwo cameras of the stereo view, and is meant to correspond to the distance betweenthe eyes.

W Move forwardS Move backwardA Strafe leftD Strage right

Mouse LookLeft click Shoot

Table 1: Basic controls

V Toggle stereo modeUp arrow Increase focal length

Down arrow Decrease focal lengthLeft Arrow Increase interocular distance

Right arrow Decrease interocular distanceP Toggle wireframe mode (cheating!)

Table 2: Advanced controls

4D3 Computer GraphicsOpenGL Project Report

4 Mikhail Volkov

Page 5: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

Part II

Developer Documentation

1 Technical Overview

Beware of the Spider is written in C# using the Tao Framework. The Tao Frameworkis a collection of bindings to facilitate cross-platform media application developmentutilizing the .NET and Mono platforms. For this game I used the Tao.OpenGl 2.1.0.12and Tao.FreeGlut 2.4.0.2 assemblies.

1.1 Why C#?

To a large extent it comes down to personal preference. I am a big fan of .NET andI was curious to see how a computationally demanding application such as a 3D gamewould cope compared with a native C++ solution, for example. Furthermore, C# is apowerful, expressive and elegant language. Throughout this report I will allude to thisclaim, giving specific examples of things I did with C# that you cannot do with C++,and how it improved the design of my game.

1.2 Why Tao?

There are several possibilities for using OpenGL with C#.

• SharpGL: I started off with SharpGL. It is very easy to use, but I found it to besomewhat buggy and unstable. SharpGL requires you to instantiate an OpenGLobject, rather than making static calls from a namespace, which felt somewhatunintuitive. It also features a Scene Graph interface which makes it much easierto work with certain OpenGL structures like cameras and textures. However, itobfuscates the underlying principles and was not good from a learning point ofview.

• CsGL: This was the second API I tried. CsGL felt much more robust thanSharpGL, but it is not supported any more and most of its user base seems tohave migrated to Tao.

• Tao: Tao has been around for a number of years and it is the most establishedOpenGl framework for .NET, with a large developer community. Tao featuresbindings for the entire OpenGL API, as well as FreeGlut and many other thirdparty libraries.

• OpenTK: OpenTK is cross-platform, offers a number of helper libraries, and isgrowing in popularity. But Tao is more mature, and OpenTK is actually based onTao so in the end I decided to use Tao and did not get a chance to try OpenTK.

5

Page 6: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 1.3 Why FreeGlut?

Finally, it is worth a mention that a .NET framework can be used with any .NETlanguage, so C++/CLI was another tempting possibility. We have the benefits of amanaged environment and the ability to drop down to native C++ for performance bot-tlenecks. This would also have been possible in combination with FreeGlut. However,I believe C# offered more advantages as a language.

1.3 Why FreeGlut?

Tao is simply a collection of bindings for the native OpenGL binaries. In order forOpenGL to work it requires a device and a rendering context, and we have exactlythe same options to choose from under Tao. These are some of the options that Iconsidered:

• Glut/FreeGlut: This is the course standard. It is fast, cross-platform and veryeasy to use. It is also very readable and a lot of examples on the internet useFreeGlut. Given that I am already deviating from the course in my choice ofprogramming language, I was reluctant to also use a different window manager,unless I saw very a compelling reason to do so.

• SimpleOpenGlControl: This is a widget that creates an OpenGl context as aWindows Forms control. It means that we can select it from the toolbox inVisual Studio and drag and drop it into our Windows Forms application. In-fact we can have multiple controls in one application. This is very powerful andI used it in the initial stages of the game development as a rapid prototypingenvironment. But ultimately it is not suitable for this sort of game as it slow,and in most cases users would prefer to play a first-person shooter in fullscreenso they probably would not need or want buttons and menus.

• Win32: This is the context used in the NeHe tutorials. It sets up a Win32environment and compares quite favorably with FreeGlut in terms of performance.Although it offers more control than FreeGlut at a low level, it also poses morepotential problems. I saw no reason to use this, other than for the sake of it.

• SDL: Another good alternative and we get the extra benefits of sound and True-Type fonts. Tao also has a wrapper for SDL so this would have been easy to do.I have no idea about performance though, and I did not see a really convincingreason to choose this over FreeGlut.

2 Implementation Details

This section will detail all the notable features in Beware of the Spider. They are listedroughly in the order I worked on them and this reflects what I believe to be the mostappropriate order in the development cycle of this kind of game.

Having chosen .NET as my development environment, I was always going to be onthe backfoot in terms of performance and I took the issue of optimization very seriously.

4D3 Computer GraphicsOpenGL Project Report

6 Mikhail Volkov

Page 7: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.1 Navigation

As well as outlining the theoretical and implementation details, where applicable I willalso provide a discussion of the steps I took to ensure that performance was optimized.

2.1 Navigation

This was the starting point in the development of the game. I started off with noth-ing except the three Cartesian axes rendered onto the screen. This was enough toimplement and test navigation.

2.1.1 Mouse Look

Being an avid fan of first-person shooters, I was keen to implement mouse look. WhenDoom first changed the face of FPS games in the nineties, millions of gamers played itwithout using the mouse. But games have come a long way since, and without mouselook I felt the game would have been difficult to enjoy.

There are several ways of approaching this. I chose to use spherical geometry(Figure 2) as it was easy to derive from first principles.

Figure 2: Spherical coordinates

FreeGlut provides two callbacks for mouse motion, glutMotionFunc and glutPas-

siveMotionFunc which take the (x, y) position of the mouse cursor on the screen asthe arguments. To implement mouse look, two angles are defined, the azimuth (θ) andthe elevation (φ). As the player moves the mouse, these two angles are calculated asthe difference between the current mouse position and the center of the screen. Theelevation is bounded between 0 and π so that the player does not turn upside down ashe looks past straight up or straight down1. Then, to convert these angles into a direc-tion vector we simply use the following expressions, which convert spherical coordinates(r, θ, φ) into Cartesian coordinates (x, y, z):

1This is typical of a first-person view. The “highest” point you can look at is straight up and the“lowest” point you can look at is straight down. You cannot “bend over backwards.”

4D3 Computer GraphicsOpenGL Project Report

7 Mikhail Volkov

Page 8: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.1 Navigation

x = r cos θ sinφ

y = r sin θ sinφ

z = r cosφ

Because the direction vector is a unit vector, we can simply set r equal to one.Listing 1 shows the C# code implementing this logic, which converts an (x, y) mouseposition on the screen into the resulting direction vector.

1 internal void MouseLook(int x, int y)2 {3 Az = x - Game.WindowCenter.X;4 El = y - Game.WindowCenter.Y;56 if (El < 1)7 El = 1;8 if (El > 179)9 El = 179;

1011 float CosAz = (float)System.Math.Cos(Az * System.Math.PI / 180);12 float SinAz = (float)System.Math.Sin(Az * System.Math.PI / 180);13 float CosEl = (float)System.Math.Cos(El * System.Math.PI / 180);14 float SinEl = (float)System.Math.Sin(El * System.Math.PI / 180);1516 Dir.x = CosAz * SinEl;17 Dir.y = CosEl;18 Dir.z = SinAz * SinEl;19 Dir.Normalize();20 }

Listing 1: Mouse look

This approach is very primitive and has a lot of drawbacks. Firstly, the mousemovement is not smooth, since it is essentially a function of the (x, y) mouse positionon the screen, which is discrete. A difference of 1 pixel is exacerbated when convertedinto a direction vector. This is not noticeable in general gameplay, but does becomeapparent if one attempts to aim very precisely at an object that is very far away.Another problem is that although the cursor is hidden and replaced by the crosshair, itis still moving around the screen so when it reaches the screen boundary the mouse lookstops working. But this only happens if the player constantly rotates in one direction,which does not occur very often in general gameplay. Lasly, this method suffers fromwhat I later discovered to be a well-established problem called gimbal lock.

The solution to all these problems was to use quaternions for rotation, using theso-called arcball method for example, but this was beyond the scope of this project.

2.1.2 WASD Movement

For movement there are two alternatives. The first is to poll for a key being down, andif it is then the position of the player is updated continuously in the update loop. The

4D3 Computer GraphicsOpenGL Project Report

8 Mikhail Volkov

Page 9: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.1 Navigation

second alternative is to poll for a key press and a key release, and to update velocityrather than position. Thus if the a key is pressed, we increase the velocity and if a keyis released we decrease the velocity; and the position of the player is updated accordingto the velocity in the update loop. I tried both, and the second one proved to be muchmore responsive so this is the method used in the game.

FreeGlut provides two callbacks, glutKeyboardFunc and glutKeyboardUpFunc, whichare called when a key is pressed or released, respectively. The Player class exposes aninterface of four boolean properties, one for each of the four WASD navigation keys,which can be set or reset depending on whether the key is pressed or released. Listing 2shows an example.

1 void KeyboardCallback(byte key, int x, int y)2 {3 switch (Char.ToUpper((char)key))4 {5 ...67 // Player movement8 case ’W’:9 player.KeyPressUp = true;

10 break;1112 ...

Listing 2: Key press example

Inside the Player class, the implementation is a little more complicated. Movementshould be independent of the direction the player is facing. Again there are two waysof achieving this. When a player rotates, we can rotate the world around the camera inthe opposite direction so that the player is always facing forward in world co-ordinates.Movement becomes simple, and the advantage is that the camera only needs to beprojected once, but this method is not very intuitive and regardless of this, I wouldneed to cast multiple projections for the stereo mode. The second alternative is to keepthe world stationary, rotating the camera when the player rotates, and to calculate thecorrect angles for movement based on the current view direction. The second methodis used in the game, and it complements the mouse look implementation quite well.We simply disregard the elevation and work out how the azimuth corresponds to worldcoordinates, and then adjust the velocity using basic trigonometry. The azimuth istaken as the angle counter-clockwise angle between the direction vector rotation aboutthe y-axis and the positive x-axis. This choice is arbitrary, but does affect the logic.The C# code implementing movement is shown in Listing 3 below.

1 float CosAz = (float)Math.Cos(Az * Math.PI / 180);2 float SinAz = (float)Math.Sin(Az * Math.PI / 180);34 Vel = new Vector3f();5

4D3 Computer GraphicsOpenGL Project Report

9 Mikhail Volkov

Page 10: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.2 Representation

6 // W key pressed7 if (KeyStates[Keys.Up])8 {9 Vel.x += CosAz;

10 Vel.z += SinAz;11 }12 // S key pressed13 if (KeyStates[Keys.Down])14 {15 Vel.x -= CosAz;16 Vel.z -= SinAz;17 }18 // A key pressed19 if (KeyStates[Keys.Left])20 {21 Vel.x += SinAz;22 Vel.z -= CosAz;23 }24 // D key pressed25 if (KeyStates[Keys.Right])26 {27 Vel.x -= SinAz;28 Vel.z += CosAz;29 }3031 Vel.Normalize().Multiply(Speed);3233 Pos.Add(Vel);

Listing 3: Movement

The velocity is normalized at the very end. This is required so that the velocity isnot additive if a player is pressing two keys at once. After this, the velocity is multipliedby the speed.

Once we have updated the position and direction vectors, we simply use gluLookAt

to display the scene.

2.2 Representation

The next step was to construct the world. The 3D model of the maze is constructed froma file, which features a 2D Nethack-style symbolic representation of the 3D model. Thisapproach has several advantages over hard-coding the maze. It allowed me to developthe game without worrying about gameplay in advance. It also allowed me to conducttesting much more easily. For example when I needed to test collision detection I couldeasily remove everything except one wall and one spider.

The representation of the labyrinth is broken down into manageable constructs. AVertex contains an (x, y, z) vector representing its position in 3D space and a (u, v)vector mapping a texture coordinate to the vertex. A Triangle contains three suchVertex objects and a Quad contains two Triangles (which are right angled and alignedalong their hypotenuses, forming a rectangle) and a normal vector to the plane that

4D3 Computer GraphicsOpenGL Project Report

10 Mikhail Volkov

Page 11: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.2 Representation

the rectangle lies on. Different sections of the maze are represented as collections ofTriangle or Quad objects.

Figure 3 shows the most basic representation of the maze in the 2D file. The @

symbol represents the player and the + symbol represents walls.From this we can see that a single wall is not flat, but has the same area as a

single empty space. In the 3D model, a wall must therefore be represented by fourQuad objects. In the most general sense, the task of converting this maze file intoa 3D model of the maze involves finding the location of each + symbol in the textfile, converting this 2D location into a 3D location in world coordinates and creatingfour Quads at that location. But it becomes immediately apparent that this is quitewasteful, as some walls will be adjacent horizontally and some adjacent vertically, andwe will end up creating more Quads than we are able to see, with no easy way todiscriminate between them. This would have made collision detection very inefficient.So the next step was to differentiate them into what I called xy-walls and yz-walls. xy-walls (- symbol) are parallel to the xy-plane and yz-walls (| symbol) are parallel to theyz-plane. In some cases we obviously need both (+ symbol). Using this representationwe have already nearly halved the number of Quads required to represent the samemaze. Figure 4 shows the resulting maze file.

+++++++++++++++++++++++++ + ++ + +++++ ++ + + ++ ++++++++++ + + ++ + + + ++ ++++++++++ ++++++++++++++ + ++ + + ++ ++++++ +++++ ++++ +++++ + ++ + + ++++ +++++++++++ ++++++++++++ + + ++ +++ +++ +++ ++ +++++++++++++++ ++ ++ +++ + ++++++ + ++ + + ++++ +++ ++++++ + + ++ +++ + ++++ ++ ++ ++ ++ @ +++++++++++++++++++++++++

Figure 3: Maze file – basic representa-tion

+-----------+----------+| | || | +---+ || | | || +--------+ | + || + | | || +--------+ +----++------+ | || | | || +----+ +-+-+ +--+ +--|| + || + + +--+ |+--------+ +---------+| + | || +-+ +-+ +++ || +-------------+ || ++ +-+ | +---|| + ++ | | |+-+ +-+ +----+ + | |+ +++ + +--+ |+ +| ++ |+ @ +---------------+++++++++

Figure 4: Maze file – using differentorientations for walls

4D3 Computer GraphicsOpenGL Project Report

11 Mikhail Volkov

Page 12: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.2 Representation

This is satisfactory, but again thinking ahead to collision detection, this is still alot of walls to check against. I decided to create different “types” of walls. As wellas allowing me to assign different textures to them, it means that an entity will onlyhave to check for collisions with only those walls that are in the entity’s proximity, thussignificantly improving efficiency. Figure 6 shows the resulting maze file. The symbolscorrespond to Figure 4.

Now we are nearly there. I introduced symbols to represent towers (groups of fourTriangles), to sit on top of some of the walls and make the maze look prettier andmore distinctive. The floors are also formed using Quads with associated textures soI introduced different symbols for different floors. Finally, I used letters (s,S,o,O,Q)to represent the different spiders in the game (the floor under the spider is the floorcorresponding to the part of the maze (level) the spider is located in). The final mazefile (Figure 7) looks rather confusing but contains all the information describing themaze and its inhabitants.

The logic for reading in the maze file and creating the world is contained in Cre-

ateMaze and CreateModel in World.cs. Lambda expressions are used to create tempo-rary methods within methods, helping to keep the overall structure of the code morereadable.

Figure 5 shows an example of the finished labyrinth model, with walls and towers.

Figure 5: The walls and towers of the labyrinth

4D3 Computer GraphicsOpenGL Project Report

12 Mikhail Volkov

Page 13: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.3 Physics and Collision Detection

#===========#˜˜˜˜˜˜˜˜˜˜#; ; :; ; #˜˜˜# :; ; : :; #========# : # :; # : : :; #˜˜˜˜˜˜˜˜# #˜˜˜˜##˜˜˜˜˜˜# : :: : : :: #˜˜˜˜# #˜#˜# #˜˜# #˜˜:: # :: # # #˜˜# :#˜˜˜˜˜˜˜˜# #˜˜˜˜˜˜˜˜˜#| + | || +-+ +-+ +++ || +-------------+ || ++ +-+ | +---|| + ++ | | |+-+ +-+ +----+ + | |/ /++ + +--+ |/ /| ++ |/ @ /---------------+////////

Figure 6: Maze file – using differenttypes of walls

ˆ===========ˆ˜˜˜˜˜˜˜˜˜˜ˆ;O’OO’O’O’O’;‘‘‘‘‘‘s‘‘‘:;’O’O’’’’QO’;‘‘‘*˜˜˜*‘‘:;O’O’’O’’O’’;‘‘s:oo‘‘‘‘:;’’#========#oo‘:‘‘*s‘s:;O’#s‘‘‘o‘‘o‘o‘‘:‘s:‘oo:;soo‘os*˜˜˜˜˜˜˜˜*‘*˜˜˜˜ˆˆ*˜˜˜˜˜*‘‘‘o‘‘‘‘‘‘:oooo::‘s:o‘s‘s‘o‘os‘‘s‘:‘‘s‘::‘*˜˜˜˜*‘ˆ˜ˆ˜ˆ‘*˜˜*‘*˜˜::‘‘‘‘*‘‘‘‘o‘o‘‘‘‘‘‘‘‘‘‘::s‘‘‘‘‘‘s*‘‘o*s‘‘*˜˜*‘s:ˆ˜˜˜˜˜˜˜˜ˆ‘o‘ˆ˜˜˜˜˜˜˜˜˜ˆ|.+.S.|...............S||...+-+.S.+-+......+++.||.+-------------+S..S..||S.++....+-+....|..+---||........+...++S|....|S|%-%,,%-%...+----+.+S.|.|/,,,,,,/++.+.S....+--+.|/,,,,,,/|S....++S......|/,,@,,,/---------------%////////

Figure 7: Maze file – final version in-cluding floors, towers and enemies

2.3 Physics and Collision Detection

At the heart of pretty much all physical interaction are just three geometry func-tions contained in Geometry.cs. SphereSphereIntersect and PlaneSphereIntersect

are boolean functions which simply return true or false depending on whether twospheres, or a sphere and a plane intersect, respectively. RayPlaneIntersect uses ray-tracing to check for the intersection of a ray and a plane, and by its nature it willalways return true unless the ray is parallel with the plane, but it is useful becauseit also returns the point of intersection and the parameter to obtain this point. Themaths behind these three methods is well documented online, in the lecture notes, andin the course textbook, so I will omit the details.

Physics in Beware of the Spider involves collision detection of entities with wallsand bullets, as well as collision detection between entities.

2.3.1 Collisions between Entities and Bullets

Collisions between entities, and collisions between entities and bullets are checked in thesame way. Entities and bullets have bounding spheres with a specific radius, tuned for

4D3 Computer GraphicsOpenGL Project Report

13 Mikhail Volkov

Page 14: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.3 Physics and Collision Detection

the best gameplay. To check for a collision we trivially check if two bounding spheresintersect. As an example, Listing 4 contains a code snippet for checking collisionsbetween spiders and bullets.

1 ...2 foreach (var s in spiders[i])3 {4 ...56 // Check if spider has been hit with bullet7 foreach (var b in bullets)8 {9 if (b.State == Bullet.States.Active &&

10 s.State == Spider.States.Alive &&11 Geometry.SphereSphereIntersect(s.Pos, s.Radius, b.Pos, Bullet.

Radius))12 {13 b.State = Bullet.States.Destroyed;14 s.HandleHit();15 continue;16 }17 }18 ...19 }

Listing 4: Spider bullet collisions

2.3.2 Collision with Walls

Entities check for collisions with Quads, two or four of which make up a wall, as dis-cussed in Section 2.2. It would have been incredibly wasteful to check for collisionsagainst every single Quad at any given time, so Quads are grouped into different col-lections, each of which has an associated set of bounds. We can think of all the Quadsin a collection as residing in a large rectangular “box”. Before checking for collisionswith each Quad in this “box” we first check if we are actually inside the “box” itself,and if we are not then there can be no collision with any of the walls therein. Thisdramatically improves performance, even if the walls are split into no more than eightsuch collections, each with an associated set of bounds.

If we are indeed within the set of bounds for a particular collection of Quads, thenwe can proceed to the actual collision detection. A plane is an infinite 2D subspace ofthe 3D world so a collision with a Quad is detected not only if the bounding sphere ofthe entity collides with the plane that the Quad is located on, but also if the boundingsphere is actually within the bounds for the Quad itself. The latter is computationallyless demanding to check, so it is checked first.

A sample of code implementing this logic to check for collisions in the xy-plane, isgiven in Listing 5 below. The same logic follows for collisions in the yz-plane.

4D3 Computer GraphicsOpenGL Project Report

14 Mikhail Volkov

Page 15: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.3 Physics and Collision Detection

1 internal static bool CheckWallCollisions(Vector3f pos, float r)2 {3 for (int i = 0; i < WallBounds.Length; i++)4 {5 // Check if the position is within the bounds for this set of quads6 bool inside =7 pos.x > WallBounds[i].min.x && pos.z > WallBounds[i].min.z &&8 pos.x < WallBounds[i].max.x && pos.z < WallBounds[i].max.z;9

10 // If not, then no need to check for collisions11 if (!inside)12 continue;1314 // Check XY collisions15 foreach (var q in WallsXY[i])16 {17 // The second vertices of the triangles in a18 // quad are always on opposite sides19 Vector3f a = q.triangles[0].vertices[1].p;20 Vector3f b = q.triangles[1].vertices[1].p;2122 // Check if position is within the bounds for this quad23 inside = (pos.x < a.x && pos.x > b.x) || (pos.x > a.x && pos.x <

b.x);24 if (inside)25 {26 Vector3f p = q.triangles[0].vertices[0].p;27 Vector3f n = q.normal;2829 // Check intersect30 if (Geometry.PlaneSphereIntersect(pos, r, p, n))31 {32 // Collision detected33 return true;34 }35 }36 }3738 ...

Listing 5: Checking for collisions with walls

To handle collisions with walls we first break up the velocity vector into two com-ponent vectors ~vx = (~vx, 0, 0) and ~vz = (0, 0, ~vz). Using these two vectors, we obtaintwo projected positions, ~px = ~p + ~vx and ~pz = ~p + ~vz, by adding the respective com-ponent vector to the position vector. Then we independently check for collisions usingthe projected positions ~px and ~pz. If the projected positions result in a collision thenwe set the velocity component in that direction to zero. Checking for collisions in thisway ensures that if the player collides with a wall in the x-direction, he is still able to“slide” along the wall in the z-direction, and vice versa2. Listing 6 below shows the

2This is the expected behavior in a first-person game

4D3 Computer GraphicsOpenGL Project Report

15 Mikhail Volkov

Page 16: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.3 Physics and Collision Detection

C# code implementing this logic.

1 Vel.Normalize().Multiply(Speed);23 // Handle wall collisions4 Vector3f projX = Vector3f.Add(Pos, new Vector3f(Vel.x, 0, 0));5 bool collisionX = World.CheckWallCollisions(projX, Radius);6 if (collisionX)7 Vel.x = 0;89 Vector3f projZ = Vector3f.Add(Pos, new Vector3f(0, 0, Vel.z));

10 bool collisionZ = World.CheckWallCollisions(projZ, Radius);11 if (collisionZ)12 Vel.z = 0;1314 Pos.Add(Vel);

Listing 6: Player collision handling

This example was for the player. Collision checking for spiders works in exactlythe same way. Collision checking for bullets is even simpler. We check the bullet’sactual position, without worrying about projected positions since there is no danger ofa bullet “getting stuck” at the point of collision – if a collision is detected, the bullet issimply removed.

2.3.3 Bullet Management

When the player fires, a bullet is created and stored in a collection. Whenever abullet hits a wall or an entity it is to be removed from the collection. You cannotremove from a collection during iteration (in C++ or C#) because it will invalidatethe iterator. So the procedure requires two iterations, one pass to check if the bullet is tobe destroyed and tag it accordingly and another pass to actually remove the destroyedbullets from the collection. Here is a typical C++ solution, assuming a simple methodcalled CheckCollisions.

1 std::list<Bullet>::iterator iter = bullets.begin();2 while (iter != bullets.end())3 {4 if (iter->CheckCollisions())5 iter->State = Bullet::States::Destroyed;6 ++iter;7 }89 std::list<Bullet> updated;

10 iter = bullets.begin();11 while (iter != bullets.end())12 {13 if (iter->State != Destroyed)14 updated.push_back(*iter);15 ++iter;

4D3 Computer GraphicsOpenGL Project Report

16 Mikhail Volkov

Page 17: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.4 Models and Animation

16 }1718 bullets = updated;

Listing 7: Removing destroyed bullets in C++

Compare that with the elegance and readability of a C# solution using LINQ, whichdoes not require a temporary container.

1 foreach(var b in bullets)2 if (b.CheckCollisions())3 b.State = States.Destroyed;45 bullets = (from b in bullets6 where b.State != Bullet.States.Destroyed7 select b)8 .ToList<Bullet>();

Listing 8: Removing destroyed bullets in C#

2.4 Models and Animation

The game features four different enemies: big spiders, small spiders, eggs which hatchinto baby spiders, and the Gargantuan Spider Queen (Spiders.cs). The four differentspiders all inherit from the abstract base class Spider (Spider.cs). Both Spider andPlayer in turn inherit from Entity (Entity.cs), which contains basic physics fields andprovides an interface for updating and drawing. The Spider class itself contains all thenecessary logic for physics, behavior and animation that is shared by all spiders.

2.4.1 Hierarchical Spider Models

Spiders are drawn using just two geometric primitives: the sphere and the cylinder.The body, the head and the eyes are formed with spheres. The legs are formed usingcylinders, a sphere for the kneecap, and a cone for the lower leg (still a cylinder, withbase width zero). GLUquadric objects were used to draw the primitives. Listing 9contains the code for drawing a sphere. The code for the cylinder is similar.

1 protected static void DrawQuadricSphere(float radius, String textureKey)2 {3 Glu.GLUquadric quad;4 Gl.glBindTexture(Gl.GL_TEXTURE_2D, Game.Textures[Game.TextureIndices[

textureKey]]);5 quad = Glu.gluNewQuadric();6 Glu.gluQuadricTexture(quad, Gl.GL_TRUE);7 Glu.gluSphere(quad, radius, 20, 20);8 Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);9 }

Listing 9: Quadric sphere

4D3 Computer GraphicsOpenGL Project Report

17 Mikhail Volkov

Page 18: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.4 Models and Animation

The spider model was created hierarchically, through trial and error. By startingwith an oval for the body it was easy to visually position the head and the eyes andmake the legs appear as natural as possible (Figures 8, 9). No modelling software wasused to create the spiders – everything was done “by hand”. Once the basic spidermodel is drawn, it is stored in a display list and called whenever a spider needs to bedrawn. Different spiders differ in scale, texture and animation parameters but asidefrom that the underlying hierarchical model is the same. The code for drawing thespider is contained in DrawSpiderAlive and DrawSpiderDead in Spider.cs.

Figure 8: Big spider

Perhaps the biggest optimization step in the game concerns the management ofdisplay lists between the different types of spiders. Ideally, we would like to havea single display list for each spider type. This is achieved easily by declaring a staticdisplay list for the Spider class which is shared by all instances. But with this approach,we encounter a problem since the derived classes use different scales and textures sothey cannot share the same display lists.

The solution once again owes to the elegance and simplicity of C#, using a languagefeature known as the static constructor. A static constructor is a method that is calledonce per class, rather than once per instance, and is guaranteed to be called before thefirst instance of the class is created.

4D3 Computer GraphicsOpenGL Project Report

18 Mikhail Volkov

Page 19: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.4 Models and Animation

Figure 9: Small spider

The logic is as follows. The Spider base class has a member handle to a display listand a generic method for generating display lists (Listing 10).

1 // Texture keys2 protected static String TextureKey;34 ...56 // Display lists7 protected int[] DisplayListsAlive = new int[40];8 protected int DisplayListDead;9 protected int DisplayListBlood;

1011 ...1213 protected static void CreateDisplayLists(14 ref int[] displayListAlive,15 ref int displayListDead,16 ref int displayListBlood,17 float animationFrameFactor,18 float animationAngleFactor,19 float animationSpeedFactor20 {

4D3 Computer GraphicsOpenGL Project Report

19 Mikhail Volkov

Page 20: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.4 Models and Animation

21 ...22 }

Listing 10: Base class member display list handles and a base class method for creatingdisplay lists

Note that the fields are unassigned and protected, and that CreateDisplayLists

takes arguments by reference.The derived spiders all have a set of static display lists. When a spider is created

(for example, the big spider), the static constructor is the first method called and itgenerates a single set of static display lists for all big spiders using the base class staticmethod CreateDisplayLists (Listing 11).

1 // Display lists2 static int[] StaticDisplayListsAlive = new int[40];3 static int StaticDisplayListDead;4 static int StaticDisplayListBlood;5 ...67 static SpiderBig()8 {9 // Assign texture key

10 TextureKey = "spider1";1112 // Generate display lists13 CreateDisplayLists(14 ref StaticDisplayListsAlive,15 ref StaticDisplayListDead,16 ref StaticDisplayListBlood,17 2, 0.5f, 4);18 }

Listing 11: Big spider static constructor and static display list handles

Note that the unassigned base class texture key is now assigned according to thederived class, and it is the static display lists that are passed to CreateDisplayLists.

Finally, the next method invoked is the instance constructor, which binds the in-stance display lists in the base class to the static display lists which have been createdin the derived class static constructor (Listing 12).

1 internal SpiderBig(Vector3f pos, Vector3f playerPos, float playerRadius)2 : base(pos, playerPos, playerRadius)3 {4 ...56 // Bind display lists7 DisplayListsAlive = SpiderBig.StaticDisplayListsAlive;8 DisplayListDead = SpiderBig.StaticDisplayListDead;9 DisplayListBlood = SpiderBig.StaticDisplayListBlood;

10 }

Listing 12: Big spider instance constructor

4D3 Computer GraphicsOpenGL Project Report

20 Mikhail Volkov

Page 21: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.4 Models and Animation

The next time a big spider is created only the instance constructor is called, whichsimply binds to the static display lists already in memory.

So in summary, each spider instance has a handle to a set of display lists, butthere are only four actual sets of display lists ever created – one for each spider type,generated by the static constructor of that class. At the same time, there is no lossof generality as we are able to make use of a single base class method for creating thedisplay lists. The end result is a very efficient use of resources and the ability to createmany spiders of different types.

2.4.2 Animation

There are two kinds of animation in the game: leg animation and blood animation.When the spider is alive, he moves around and the legs animate to reflect the movement.When the spider dies, he lies stretched out and a pool of blood slowly appears aroundhim. All animation is pre-determined and stored in display lists before the spiders areever created, as already discussed.

For leg animation, this is done by having an array of display lists, rather than asingle display list. The array is dynamically indexed through by a global frame counterincremented in glutIdleCallback, the main update loop. Furthermore, the last framein the animation sequence is designed to match the first frame, which makes it look likethe spider is constantly moving its legs. The Spider class has 12 different angles and 2additional flags and for each of the 40 frames in the leg animation sequence, the anglesare pre-calculated using the current frame number and three additional parameters.It is these parameters (frame factor, angle factor and speed factor) which allow thesame method to create different animation sequences for different spider types. Oncethe angles are calculated, the current frame is drawn using those angles and stored inthe display list array. The angles control 3 different points of rotation for each leg,with different delays between them, and make it look like the spider is walking around.When the display lists are generated we can simply call them using the current framenumber modulo 40, to create a smooth and continuous animation sequence. Listing 13contains the code for generating the leg angles. The logic is very ad-hoc and difficultto explain. It took hours to write, and it works just because it works.

1 protected static void CalculateAngles(long frame,2 float animationFrameFactor, float animationAngleFactor, float

animationSpeedFactor)3 {4 l1inc = 30 + (float)(frame % (5 * animationSpeedFactor)) *

animationAngleFactor;5 l2inc = 45 - (float)(frame * 2 % (10 * animationSpeedFactor)) *

animationAngleFactor;6 l3inc = 00 + (float)(frame % (5 * animationSpeedFactor)) *

animationAngleFactor;7 l1dec = 40 - (float)(frame % (5 * animationSpeedFactor)) *

animationAngleFactor;

4D3 Computer GraphicsOpenGL Project Report

21 Mikhail Volkov

Page 22: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.4 Models and Animation

8 l2dec = 25 + (float)(frame * 2 % (10 * animationSpeedFactor)) *animationAngleFactor;

9 l3dec = 10 - (float)(frame % (5 * animationSpeedFactor)) *animationAngleFactor;

1011 r1inc = 30 + (float)((frame + (5 * animationFrameFactor)) % (5 *

animationSpeedFactor)) * animationAngleFactor;12 r2inc = 45 - (float)((frame + (5 * animationFrameFactor)) * 2 % (10 *

animationSpeedFactor)) * animationAngleFactor;13 r3inc = 00 + (float)((frame + (5 * animationFrameFactor)) % (5 *

animationSpeedFactor)) * animationAngleFactor;14 r1dec = 40 - (float)((frame + (5 * animationFrameFactor)) % (5 *

animationSpeedFactor)) * animationAngleFactor;15 r2dec = 25 + (float)((frame + (5 * animationFrameFactor)) * 2 % (10 *

animationSpeedFactor)) * animationAngleFactor;16 r3dec = 10 - (float)((frame + (5 * animationFrameFactor)) % (5 *

animationSpeedFactor)) * animationAngleFactor;1718 swapl = (frame % (10 * animationSpeedFactor)) < (5 *

animationSpeedFactor);19 swapr = (frame + (5 * animationFrameFactor)) % (10 *

animationSpeedFactor) < (5 * animationSpeedFactor);20 }

Listing 13: Generating animation angles

Once we have the angles for the current frame, we draw the legs in DrawSpiderAlive

(Listing 14). The code below shows just one side of the body. The swap flags are checkedbefore deciding which angle to use and determine whether the leg is moving “forwards”or “backwards”. This is essentially what gives rise to the pendulum-like motion.

1 #region LEGS2 Gl.glPushMatrix();3 {4 Gl.glRotatef((swapl ? l1inc : l1dec) / 2 + 5, 0, 0, 1);56 Gl.glRotatef((swapl ? l1inc : l1dec), 0, 1, 0);7 DrawLeg(85 + (swapl ? l3inc : l3dec), 2, TextureKey);89 Gl.glRotatef((swapl ? l2inc : l2dec), 0, 1, 0);

10 DrawLeg(95 + (swapr ? r3inc : r3dec), 2.2f, TextureKey);11 Gl.glRotatef((swapl ? l3inc : l3dec), 0, 1, 0);1213 Gl.glRotatef((swapr ? r1inc : r1dec), 0, 1, 0);14 DrawLeg(95 + (swapl ? l3inc : l3dec), 2.2f, TextureKey);1516 Gl.glRotatef((swapr ? r2inc : r2dec), 0, 1, 0);17 DrawLeg(85 + (swapr ? r3inc : r3dec), 2.1f, TextureKey);18 }19 Gl.glPopMatrix();20 #endregion LEGS

Listing 14: Drawing the spider legs using animation angles

4D3 Computer GraphicsOpenGL Project Report

22 Mikhail Volkov

Page 23: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.4 Models and Animation

When a spider is killed, he is drawn as an inanimate carcass. The dead spider isdrawn the same way as the living spider, but with different angles for the legs and adifferent height for the body. Again, this was determined through trial and error toachieve the most convincing results. The second aspect of animation is to draw whatlooks like an expanding pool of blood coagulating around the dead spider. Althoughit may look convincing (Figures 10, 11), the animated blood is actually somewhat of ahack. The effect is achieved by rendering seven spheres below the ground and drapingthem with a blood texture. Then by raising these spheres slowly we expose their topsabove ground level, making it look like a pool of thick blood. The spheres are storedin a single display list and are not animated as such. The animation effect is achievedsolely through changing the position of the spheres with time.

Figure 10: Dead spider carcass lying in a pool of blood

2.4.3 Animated Sky

Lastly, it is worth mentioning the animated sky. Although better looking methodsexist which construct a dome from individual vertices, using a sphere proved verysimple and looks great. The sphere is created using a GLUquadric, textured, and scaledto fit around the world model (Listing 15). The sky is then stored in a display list androtated according to the current frame to create the effect of moving clouds.

4D3 Computer GraphicsOpenGL Project Report

23 Mikhail Volkov

Page 24: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.4 Models and Animation

Figure 11: Some more arachnid cadavers...

1 static void DrawSky()2 {3 Gl.glPushMatrix();4 {5 Gl.glBindTexture(Gl.GL_TEXTURE_2D, Game.Textures[Game.TextureIndices

[TextureKeySky]]);6 Glu.GLUquadric sky = Glu.gluNewQuadric();7 Glu.gluQuadricTexture(sky, Gl.GL_TRUE);8 double radius = (double)Math.Min((decimal)MazeWidth, (decimal)

MazeHeight) * ModelScale;9 Glu.gluSphere(sky, radius, 10, 10);

10 Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);11 }12 Gl.glPopMatrix();13 }

Listing 15: Sky sphere

4D3 Computer GraphicsOpenGL Project Report

24 Mikhail Volkov

Page 25: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.5 Gameplay and AI

2.5 Gameplay and AI

2.5.1 Spider Bite Handling

The player must somehow handle being bitten by a spider. In terms of implementation,the spider knows nothing of the player and the player knows nothing of the spider butthe two must somehow manage this situation. In C++ this would typically be handledin a top level module, with both classes providing some form of public interface. InC# this can be implemented quite tersely using events and event handlers. The Spider

class declares an event which is triggered when a Spider object comes into contact withthe player.

internal delegate void SpiderBiteDelegate(float damage);internal static event SpiderBiteDelegate SpiderBite;

The Player class subscribes to the event with an event handler, the signature forwhich is defined by the delegate.

Spider.SpiderBite += new Spider.SpiderBiteDelegate(onSpiderBite);

Whenever the spider detects a collision with the player it fires the SpiderBite event,by simply calling it as a method.

SpiderBite(Spider.Damage);

The player will handle the event being fired with a call to the method onSpiderBite.Everything happens automatically and no top-level logic is involved here. All lateralinteraction between classes is handled by those classes. Furthermore, encapsulation ispreserved perfectly as, despite Spider declaring the event as internal, the only actionother classes can take is to subscribe or unsubscribe from it, and no data is ever exposedunnecessarily.

2.5.2 Spider AI

Spiders are initially asleep but they have a sense of smell. Internally, “smell” is avector from the spider’s position to the player’s position (~s = ~pplayer − ~pspider). Aspider will wake up when it has direct sight of the player. This is established withthe aid of a method in World.cs called DistanceToNearestWall. This method takes astart point (the spider’s position), an end point (the player’s position) and a directionvector (the “smell” vector) and determines whether the direction vector traced fromthe start position in the positive orientation intersects a wall before it reaches the endposition, and if so, what the distance is to the nearest wall it intersects. To achieve this,the method uses the Geometry function RayPlaneIntersect, which has already beenintroduced in Section 2.3. Because the walls being checked are only those boundedwithin the rectangle formed by the start and end positions at its diagonals, the methodis very efficient. Having calculated this distance dmin, we simply check if |~s| < dmin.If this is the case then we conclude that the smell vector does not intersect any walls

4D3 Computer GraphicsOpenGL Project Report

25 Mikhail Volkov

Page 26: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.6 Stereo Display Mode

(i.e. there are no walls between the spider and the player), and therefore the spider hasspotted the player and will awaken.

Once awakened, the spider will pursue you by following your smell. If the spider isbig and slow, it is possible to run away and hide behind a wall, for example. In thatcase, the spider will move around randomly until it sees you again. However, if a smallbut fast spider spots you, it will almost certainly not be possible to escape and thespider will give chase until one of you is dead.

Introducing this basic form of AI not only makes the game that little bit moreinteresting, but also serves as a major performance safeguard. While spiders are asleepthey do not move, and consequently they do not need to check for collisions or updateany physics. By the time you succeed in awaking more than just a couple of spiders,some of them (or you!) will already be dead so at no point are there more than justa few spiders being actively updated. The logic for spider AI is contained within theUpdate method in Spider.cs.

2.5.3 Spider Eggs

The class SpiderBaby adds another special feature to the game. Spider babies areinitially lying around in the form of unhatched eggs. Eggs are randomly positionedwithin their vicinity and given random rotations as well as ochre yellow eggshell texturesto make them look realistic (Figure 12). If you shoot or step on an egg, the spider babywill hatch and start attacking you. This can be quite scary and entertaining!

The logic for unhatched spider babies is made possible through polymorphic over-rides of the Update and Draw methods defined in the SpiderBaby class. Once a spiderbaby is hatched, it will revert to the base class calls and behave just like a normalspider.

2.6 Stereo Display Mode

Beware of the Spider features an anaglyph stereo mode, allowing it to be viewed in3D. The idea behind stereo is quite simple – use two cameras, instead of one, whichsimulate two human eyes, and then somehow make the left eye see the left view andthe right eye see the right view. Many new techniques for doing this have emergedas stereoscopic technology becomes increasingly popular, but none are as old-school asred-cyan anaglyph!

The correct way to create two stereo views is to align the two cameras parallel toeach other (Figure 13). We cannot simply rotate one camera into the position of theother one, subtending an arc, as despite also appearing stereoscopic, this will introducevertical disparity which becomes more pronounced near the edges of the scene causingvisual discomfort for the viewer.

The perpendicular distance between the two cameras is called the interocular dis-tance and should correspond to the distance between the human eyes. The focal length(which must be the same for both cameras) determines the point of no parallax in thestereo scene. At this point the two views coincide, essentially creating a mono view,

4D3 Computer GraphicsOpenGL Project Report

26 Mikhail Volkov

Page 27: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.6 Stereo Display Mode

Figure 12: Spider eggs

and it this plane that the viewer perceives to be at the same depth as the surface ofthe screen. The stereoscopic effect is achieved through positive and negative parallaxoccurring on planes in front or behind the plane of no parallax.

By controlling the focal length of the cameras we control how much of the sceneis perceived to be “going into” the screen and how much “coming out of” the screen.Finding the right focal length is in many ways an art form and comes down to personalpreference. Things coming out of the screen are more pronounced and more striking,but as a general rule we should avoid objects coming out of the screen if they are notwholly within the physical frame bounding the screen itself, as this creates a visualparadox since the frame of the screen is the plane of no parallax. Most of the time thewalls will be cut by the edges of the screen, so most, if not all, of the scene should begoing into the screen.

Figure 14 contains a screenshot of the game in stereo mode. The towers illustratethese concepts particularly well. The parallax of the tower which is further back ismore pronounced than that of the towers which are closer.

The implementation of the stereo views is as follows. First we calculate the rightvector by taking the cross product of the player’s view direction and up vector (~r =~d × ~u). Then we translate the player’s actual position into the position of the twocameras to obtain ~pl = ~p −

(~r × δ

2

)and ~pr = ~p +

(~r × δ

2

), where δ is the interocular

4D3 Computer GraphicsOpenGL Project Report

27 Mikhail Volkov

Page 28: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.6 Stereo Display Mode

Figure 13: Stereo projection

distance. Now we can create the stereo view by drawing the same scene at each position.The theory behind producing correct anaglyph images is actually quite involved. If

~a is a pixel in the anaglyph image and ~l and ~r are pixels in the left and right images,respectively, then

~a = SL ~l + SR ~r (1)

where SL and SR are 3×3 transformation matrices. If we now consider this operationto be a map S : R6 → R3 then we can reduce (1) to a single linear operation

~a =[SL SR

]( ~l~r

)(2)

= B

(~l~r

)(3)

which is fully parametrized by the 3×6 matrix B. This is the canonical form of theanaglyph algorithm. Different anaglyph techniques differ only in the transformationmatrix B used, and some are extremely complicated but the simplest method knownas the Photoshop algorithm is quite easy to implement and works very well. The redchannel of the left image is combined with the blue and green channels of the rightimage by simple addition so that

B =

1 0 0 0 0 00 0 0 0 1 00 0 0 0 0 1

(4)

4D3 Computer GraphicsOpenGL Project Report

28 Mikhail Volkov

Page 29: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.6 Stereo Display Mode

Figure 14: Screenshot of the game in stereo mode

In OpenGL this is achieved quite easily by using the function glColorMask to turnthe required channels on and off. Listing 16 contains the code for creating a stereoscene and displaying it as an anaglyph using the Photoshop algorithm.

1 void DisplayCallback()2 {3 // Stereo projection maths4 player.Dir.Normalize();5 Vector3f focus = Vector3f.Add(player.Pos, Vector3f.Multiply(player.Dir,

FocalLength));6 Vector3f right = Vector3f.Cross(player.Dir, player.Up).Normalize();7 right.Multiply(IOD).Divide(2.0f);89 // Clear color and depth buffers

10 Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);1112 // Reset color mask13 Gl.glColorMask(Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE);1415 #region LEFT VIEW

4D3 Computer GraphicsOpenGL Project Report

29 Mikhail Volkov

Page 30: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

2 IMPLEMENTATION DETAILS 2.6 Stereo Display Mode

1617 // Apply left view projection18 Gl.glMatrixMode(Gl.GL_PROJECTION);19 Gl.glLoadIdentity();20 Glu.gluPerspective(Aperture, (double)WindowWidth / (double)WindowHeight,

0.1, 1000);21 Glu.gluLookAt(22 player.Pos.x - right.x, player.Pos.y - right.y, player.Pos.z - right

.z,23 focus.x, focus.y, focus.z,24 player.Up.x, player.Up.y, player.Up.z);2526 // Draw sky in mono using the left view27 Gl.glMatrixMode(Gl.GL_MODELVIEW);28 World.DrawMono();2930 // Apply the red filter31 Gl.glColorMask(Gl.GL_TRUE, Gl.GL_FALSE, Gl.GL_FALSE, Gl.GL_TRUE);3233 // Draw the left view34 Gl.glClear(Gl.GL_DEPTH_BUFFER_BIT);35 Gl.glMatrixMode(Gl.GL_MODELVIEW);3637 ...3839 #endregion LEFT VIEW4041 #region RIGHT VIEW4243 // Apply right view projection44 Gl.glMatrixMode(Gl.GL_PROJECTION);45 Gl.glLoadIdentity();46 Glu.gluPerspective(Aperture, (double)WindowWidth / (double)WindowHeight,

0.1, 1000);47 Glu.gluLookAt(48 player.Pos.x + right.x, player.Pos.y + right.y, player.Pos.z + right

.z,49 focus.x, focus.y, focus.z,50 player.Up.x, player.Up.y, player.Up.z);5152 // Apply the cyan filter53 Gl.glColorMask(Gl.GL_FALSE, Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE);5455 // Draw the right view56 Gl.glClear(Gl.GL_DEPTH_BUFFER_BIT);57 Gl.glMatrixMode(Gl.GL_MODELVIEW);5859 ...6061 #endregion RIGHT VIEW6263 // Reset color mask64 Gl.glColorMask(Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE);

4D3 Computer GraphicsOpenGL Project Report

30 Mikhail Volkov

Page 31: 4D3 Computer Graphics OpenGL Project Report · 04/05/2013 · 4D3 Computer Graphics OpenGL Project Report ... Department of Electronic & Electrical Engineering Trinity ... and leaving

4 FINAL REMARKS

6566 ...6768 // Redraw display69 Glut.glutPostRedisplay();70 Glut.glutSwapBuffers();71 }

Listing 16: Anaglyph stereo view using the Photoshop algorithm

The actual code for drawing the models and the code for drawing a mono view havebeen omitted for brevity. Notice that since the sky is very far away it only needs to bedrawn in mono. It would have been more correct to translate to the center to draw thesky, but it is more efficient to simply draw it using one of the two stereo views, and thedifference is unnoticeable.

3 Sources

Most of the textures came from the Shoot-em-up Project (http://shoot.sourceforge.net) and some from Google image searches. All source code is entirely my own work.

4 Final Remarks

Making this game was a lot of fun. I tried to touch on most of the major issues pertinentto 3D game design. In particular, I enjoyed implementing mouse navigation from firstprinciples, as we studied spherical coordinates for two years and this was the first timeI was able to see it applied in a meaningful context.

In the end I also was surprised at how well C# coped in terms of performance. Idon’t know how much faster this game would have turned out in C++, but in generalI think .NET compares quite favourably.

Although I set off intent on overcoming my fear of spiders, it is sure to backfire asstaying up too long writing code often causes residual nightmares...

4D3 Computer GraphicsOpenGL Project Report

31 Mikhail Volkov