Welcome to EDrive 0.1 (Proof of Concept multiplatform release) -------------------------------------------------------------- This is a free program, supplied without warranty of any kind. Although I really can't see how you could damage anything with it, use it at your own risk. You can give it to anyone you like - just please include this file with it. It was written by Evan Weaver, Seneca College of Applied Arts and Technology, Toronto, Ontario, Canada, as the first stage of what is planned to be an open source driving simulator program designed for use as preparation for participation in a high performance driving school. EDrive 0.1 runs in Windows, requiring a DirectX 9 compatible video card, and in Linux (using the X Window System), where a video card that has an accelerated OpenGL (or Mesa) driver is recommended. EDrive 0.1 is quite preliminary, and merely demonstrates the most basic functionality necessary as a foundation for the real project. You get to drive around and around a single, simple track as much as you like. When you get bored, you stop. There are no real physics involved, only a really cheesy attempt to keep you from just flooring it all the way around the track. The source code for EDrive 0.1 is included. Although I have copyrighted it, it is free software, and you can modify it and play around with the code to your heart's content. I put the copyright notices in for one reason only: I plan to release future versions under a different license (probably the GNU GPL), and I don't want you to be able to claim it is yours, not mine, and try to prevent me from doing that. Otherwise, you can do with it what you want, including modify it for use as a commercial product (something I can also do). The next phase of the project will be to make the driving physics more realistic. Once that is done, I'll make some tracks closely resembling tracks I have experience driving (hopefully Mosport, Shannonville and Cayuga, which are all close to me in Southern Ontario). After that, there are endless things I could do, such as put some graphical sizzle into it, back-port it to DirectX 8.0 (to support more video cards, as I am finding out that many video cards do not have DirectX 9 drivers), add force feedback effects, add AI competition, and on, and on. Minimum Requirements -------------------- I have no idea what the minimum requirements are. The most minimal machine I have tried it on is a 450 MHz Pentium III with 128MB RAM and a 32 MB nVidia TNT2 video card, and it runs quite well there. I have tried a few machines under Windows that lack a DirectX 9 compatible video card (such as the old Voodoo2 card I have), and it won't run. Until a backport to an earlier version of DirectX is done, consider the Windows minimum to be any PC with a 3D accelerated video card supported by DirectX 9 (and with DirectX9 installed, of course). I have also tried a 450 MHz Pentium laptop under Linux with a video card lacking 3D acceleration, and found that I had to avoid using textures on the 3D objects in order to get a barely drivable frame rate. While you can use the keyboard to drive, a joystick or a steering wheel is recommended. Again, whatever joystick you use should be compatible with DirectX 9 for the Windows version, or supported by your version of Linux for the Linux version. I would really like to hear from you if it does or doesn't work on your system, along with details of your hardware. Windows Installation -------------------- If you don't already have DirectX 9 on your system, go to http://www.microsoft.com/directx to get it and install it on your system. After that is done, place all the EDrive files in a folder somewhere. (You do not need the "source" subdirectory or any of the files in it in order to sun the program, however). C:\Program Files\EDrive_0_1 would be a good place, but it really doesn't matter where. (If you got the zip file, copy the folder EDrive_0_1 to C:\Program Files. If you got the files individually, you will have to make the folder yourself, and copy the files to it). That's it. EDrive 0.1 is now installed on your system. To remove EDrive 0.1, simply delete the folder. The files are: edrive.exe - the program engine.wav - a WAV file containing the very gutteral engine noise wheel.bmp - the bitmap for the wheels grass.bmp - the bitmap for the grass road.bmp - the bitmap for the road track.txt - the track description readme.txt - this file As well there is an optional subdirectory named "source", which contains the source code for the program and will only be of interest to programmers. To run EDrive, click on edrive.exe (or type edrive from the command line after changing to the directory in which it resides). Linux Installation ------------------ EDrive requires a Linux installation with XFree86 (4.0 or later should do, but most of the testing was done with 4.3). If you have an nVidia 3D accelerated video card, you should also get the nVidia drivers directly from www.nvidia.com. EDrive also requires OpenAL (an open source audio library). (If your Linux distribution lacks OpenAL, you can get a copy from the EDrive website.) Most of the testing was done with Slackware 9.1 (where it was necessary to install OpenAL) and SUSE 9.0 (which already has OpenAL). I personally use and recommend Slackware, but then again I don't mind Slackware's text-based installation. Many of my colleagues use and recommend SUSE. Presuming that XFree86 (which includes OpenGL/Mesa and GLUT) and OpenAL are installed, all you need to do is copy the edrive files to a directory somewhere. The required files are: edrive - the program engine.wav - a WAV file containing the very gutteral engine noise wheel.bmp - the bitmap for the wheels grass.bmp - the bitmap for the grass road.bmp - the bitmap for the road track.txt - the track description readme.txt - this file There is also an optional subdirectory named "source", which contains the source code for the program, and is only necessary if you need to rebuild the program if the one supplied doesn't work with your system. For example, if you got the edrive_0_1.tgz "tar-ball" package, you could put EDrive in a directory named edrive_0_1 simply by issuing the command tar -xzf edrive_0_1.tgz from the directory in which you want the edrive_0_1 directory to be located. If the edrive program won't execute, you may need to rebuild the program to suit your particular Linux. In that case, read the directions in the readme.txt file in the "source" subdirectory (summarized in the "Notes for Programmers" section below). Run EDrive by running the edrive program from the directory in which you placed it. Operating EDrive ---------------- When you start the EDrive program, a window, in which you will already be driving, will appear. Do not be alarmed when you see just two wheels and no car body - all EDrive 0.1 shows are the wheels, and the initial view position is from behind the front wheels. The following keys are used to drive the car: A - accelerate Z - brake Left arrow - turn left Right arrow - turn right C - change the camera angle Escape - go to the EDrive menu There are four camera angles: from behind the front wheels, from behind the rear wheels, from the right and from way overhead. The C key cycles between these, but each time you start EDrive, you are returned to the view from behind the front wheels. You will find that you have to slow down a bit for the corners - too fast and you'll just go straight, although if you suddenly slow down at this point the car will likely turn too much. There is no penalty for driving off the road, so make it a challenge for yourself to try to make a lap without putting a wheel off. The environment is quite small, and there is nothing to stop you from driving off the edge of the "world". If you do so, simply turn around and drive back toward the world. There will be a jitter as you cross back into reality. On the other hand, feel free to climb up the hill in the centre of the track - there is a big asphalt field up there to do skidpad exercises on. The following keys are used to navigate the menus: Up arrow - move up one menu item Down arrow - move down one menu item Left arrow - change current item (if appropriate) Right arrow - change current item (if appropriate) Enter - select current item (if appropriate) Escape - go back to the previous menu (or resume driving) The main menu lets you resume driving, exit the program, go to the controller menu or go to the display menu. The controller menu lets you change the driving controller from the keyboard to a joystick or steering wheel. If you do change the controller, you do not necessarily have to pick the Configure option to configure it - EDrive may guess correctly how it should be used. However, if the controller doesn't work correctly, go back to the controller menu and pick the Configure option. This will give you instructions on when to do what with the controller so that EDrive will know how your controller turns, accelerates and brakes. Throughout this sequence you will be repeatedly asked to "center" the controller. You do not have to be very precise in the centering of the controller. EDrive does not get the center position from your centering activity, it simply wants the controller approximately neutral and still so that it can correctly detect the direction of the next axis you will be asked to move. Note that if you press Escape when asked to move a particular axis, that axis will be disabled and you will be able to use the appropriate key (mentioned above) to control that axis, while using the controller for the other axes. There is no provision to change the keys used to drive using the keyboard. The display menu lets you change the display from a window to full screen. In the Windows version, you will be offered the available resolution/colour depth/refresh rate combinations. Once you press Enter on a particular combination, the display will change. In the Linux version, you simply have a choice between running in a window and running fullscreen at the resolution the X Window System is using. Any changes of controller or display are saved so that the next time EDrive is started they will still be in effect. [If you want to cancel any changes and go back to the default keyboard/window setup, simply delete the file "edrive.cfg".] In the Windows version, there is no provision for changing the size of the window, if you run in windowed mode. However, you may try resizing the window, which will simply stretch the image to fit the new window size, if that turns you on. In the Linux version, you may resize the window in windowed mode, which will broaden or narrow the field of view accordingly. Howeverm the new window size is not saved, and the next time you start EDrive, it will return to its original dimensions. Troubleshooting --------------- If something goes wrong when running EDrive, look for a file named "error.log". This text file may contain some clues as to what when wrong. It can be viewed with any text file viewer, such as Notepad in windows, or the "more" command in Linux. In the Linux version, if you are using a graphics card lacking supported 3D acceleration, and EDrive runs too slowly to be useful, you can speed things up by deleting the texture files (e.g. "rm *.bmp"), at the cost of an even more boring looking environment. Playing around -------------- Although experienced programmers may want to modify the program (see the "Notes for Programmers" section below), there are things that non-programmers can do to make your EDrive installation better. You can replace the bitmap files with good ones. For example, grass and road bitmaps that actually tile without a noticeable pattern would be nice. Even better would be a WAV file that has a real (steady) engine noise rather than my gurgling. Or, you could create your own track. Here is a look at the supplied track.txt: Line 1 is: 118 .3 .3 .3 road.bmp This means the first shape (which happens to be the road) is made up of 118 triangles. Its color is composed of 30% red, 30% green and 30% blue (which happens to be gray) and it uses the bitmap file road.bmp as its texture. Line 2 is: (20.000,2.000,10.000)[20.000,10.000] This means that the first point of the first triangle has (x, y, z) coordinates (20.0, 2.0, 10.0), where x is left/right, z is in/out and y is up/down. This point has 2-D texture wrapping coordinates (20.0, 10.0). The following 2 lines have the other two points of the first triangle, in clockwise order. The next 351 lines have the points for the remaining 117 triangles. Following that, the pattern repeats again for another shape, and so on. In this version of EDrive, there is a hardcoded limit of 1000 triangles in total, with a maximum of 20 different shapes. (The supplied track has 260 triangles in two shapes, the road and the grass). The shape most likely to be driven on should appear first, as height information is determined by a linear search through all the available triangles, and the sooner the triangle is found, the less bogged down the program will get. Now, creating your own track could be a painstaking and probably painful process. But it is doable (I did it!), and it could be trivial for someone who is experienced at manipulating 3D modeling software. Contact, but don't whine ------------------------ Feel free to contact me at evan.weaver@senecac.on.ca, but here are some things (other than things mentioned earlier) that you don't need to tell me: - The track/bitmap/wheels/sound suck (I already know this). - The front wheels wobble if you turn on a hillside (I know where my calculations are wrong, but I plan to completely change how the wheels are located during the next phase, so I'll live with the wobble for now). - I have to press the right arrow key like a hundred times to change the resolution (I haven't put a key repeat in yet). - The mouse doesn't work (I haven't put any mouse support in yet). - The physics are dreadful (hey, I already mentioned this). - I can't change gears or go into reverse (it must be a gokart). - Sometimes when I change the display resolution, the program crashes (I can't solve this one, particularly in Linux, as the various available X servers vary in their ability to handle this. However, the new display choice will be saved, and if you restart the program it will be at the newly chosen setting. If any has any ideas how to fix this, I am eager to hear from you). I am, however, very interested in compatibility problems, and would like to hear about any you encounter. Have fun, and thanks for your help in trying it out. Evan Weaver, evan.weaver@senecac.on.ca Seneca College 1750 Finch Ave. E. Toronto, Ontario, Canada Notes For Programmers --------------------- 1. As mentioned before, the source code is included with EDrive 0.1. You may do with it what you want as long as you don't take away my right to do with it what I want. This is considerably less restrictive than an open source license. However, future versions of EDrive will likely be Open Source (which means that modifying them will have some behavioural obligations), so if you want to build a commercial, closed source project out of EDrive, don't wait for the next update! 2. I'm giving away this preliminary version because, frankly, it doesn't do much. In other words, you are getting what you paid for. Nonetheless, it might be useful to see a relatively simple cross platform gaming project, for those who are interested in creating something like that. For that reason, EDrive is not obsessively designed, something which would make the code much cleaner but also more obfuscated (by hiding details under layers of design). Rather, I have used objects when it is convenient and obvious to do so, but have not been a slave to object oriented design practices. The objects I have made are as simple as possible without regard to the ways in which the program might evolve. The way I look at it is that until I really understand all the issues involved (most of which are mired in technical detail), I really can't do a proper OO design, because I can't yet adequately predict where I am going to have fitment problems. For example, in the original Windows code I wrote, I was able to treat the keyboard and the graphics output independently, but when I ported it to Linux, the GLUT routines I used to access the keyboard tie the keyboard operation to the display window, which changes when the program goes fullscreen, something which I didn't appreciate until I tried to move the code to Linux. So what I have are quite simple objects, which will be easy enough to grow and/or restructure as necessary as the project evolves. 3. The source code contains both the Windows and the Linux code. There is one place, in one file, platform.h, that controls which code will be used: // Uncomment one of the two following lines: #define ED_LINUX 0 //#define ED_LINUX 1 (ED_LINUX determines whether EDrive is being compiled for Linux or not, where 0 means not. The lines as shown above will then cause the Windows code to be compiled). You will have to edit these lines manually if the file is not correct for the platform on which you are trying to compile. In both environments, a "make" utility is used to automate the compile. To make life easier, in Linux issue the command: cp Makefile.linux Makefile and in Windows issue the command: copy Makefile.windows Makefile to set things up for the "make" utility. 4. The Windows code was written for Visual Studio .NET with the DirectX 9.0 "Summer 2003" Software Development Kit. Note that the Summer 2003 update changed a few API calls, and the EDrive code won't compile with the original 9.0 SDK. You can get the Summer 2003 update from http://msdn.microsoft.com/directx. Actually, saying I use Visual Studio .NET is an overstatement - all I use is the command line C++ compiler (CL) and the "make" utility (NMAKE). If you have installed Visual Studio .NET, then in the Windows Start Menu, you will find a Visual Studio .NET Tools sub-choice, which has a further sub-choice called the Visual Studio .NET Command Prompt that opens a command prompt window with the PATH and other environment variables set up to use NMAKE and CL properly. Alternatively, you can find the VSVARS32.BAT batch file in the Visual Studio .NET Common7\Tools subdirectory, and run that from a regular command prompt. Actually, you probably want to modify that batch file to place the DirectX 9 SDK include directory at the start of the INCLUDE environment variable setting, and the DirectX 9 SDK lib directory at the start of the LIB environment variable setting. Once you have a command prompt that works properly, issuing the command: nmake will compile EDrive. If that doesn't work, reread notes 3 and 4. If it does work, copy the resulting executable program to the EDrive directory, e.g. copy edrive.exe "c:\program files\edrive_0_1\" 5. The Linux code was written for the GNU C++ compiler. You will probably need to manually modify the following section of the Makefile: # You will probably need to modify the following three lines, depending # on where the OpenAL libraries, OpenAL #include files and X11 (including # OpenGL/Mesa) libraries, respectively, are located on your system. # OPENALLIBDIR = /usr/local/lib OPENALINCLUDEDIR = /usr/local/include X11LIBDIR = /usr/X11R6/lib depending on where things are on your system. Good thing you are a programmer - you should be able to find them! Once that is done, you should be able to issue the command: make which will make the program binary. If that doesn't work, reread notes 3 and 5. Otherwise, copy the resulting binary to the edrive directory, e.g. cp edrive .. 6. The frame drawing logic does not depend on a particular frame rate for moving the car. Rather it checks the time elapsed since the last frame was drawn, and calculates distances based on that. This suffers from potential inaccuracies at both ends of the scale: with very fast frame rates, the timer granularity (1 millisecond) can lead to rounding error, and with very low frame rates, motion occurs in straight lines between frames. The too-fast frame rate is easily solved: the frame drawing function checks to see how much time has elapsed since the last frame, and doesn't do anything unless a certain threshold has been reached. I recommend 5 milliseconds (which means an upper limit of 200 frames per second), but it can be set to 0 if you want to see how many frames your hardware can crank out. While the computations may not be technically as accurate, it seems just as drivable with the threshold set to 0 (where my PC outputs well over a thousand FPS). The too-slow frame rate is a problem in any case; if you are getting only one or two frames per second, it won't be drivable even if the movement were properly computed. Many games use frames to control motion, with a timer check to output frames no faster than a specific, predetermined rate. The problem comes when the hardware can't keep up with that rate and the game action goes into slow motion, a situation which is just not acceptable for a driving game. Frame rates as low as 10 fps can still be drivable as long as the distances are computed based on time rather than a fixed per-frame amount. 7. There are several areas where a lot of optimization could occur. The most obvious are the way drawable objects (called "things" in the code) are managed and the way the terrain is handled. 8. Each "thing" keeps its own position and texture. One drawback to this is that if several things use the same texture data, that data gets loaded several times. A similar drawback is that if you want the same item replicated throughout the world, you must have several instances, which is wasteful, or else you must track the various positions separate from the things themselves, and move one instance around to multiple locations as you draw each frame. For example, the current implementation has four wheels, which are all identical except in location, and each has its own vertex and texture information, when all we really need is one set of vertices and one texture, but four locations. Once the number of objects grows, this will probably be addressed, with a corresponding increase in complexity, of course. 9. The terrain is currently stored as a series of triangles, rather than as a much more compaact triangle strip. Also, the terrain triangle data is double-stored, one version for rendering which potentially lives on the video card, and another for terrain-following calculations which is stored in regular memory for fast access by the CPU. And the terrain vertex normals are computed (to be perpendicular to the triangles on which they are located) which causes abrupt lighting changes between triangles. Eventually it might be nice to supply the vertex normals as data, or else average the vertex normal between the triangles a given vertex touches, either of which would make the terrain look smoother. The terrain-following logic is pretty simplistic: the projected new location is computed using the current direction of travel, determining a point which may be above or below the terrain. A plumb line (perfectly vertical) is used to find the terrain height directly above or below the projected location, and that is used to determine the actual new location. Ideally a line perpendicular to the terrain should be used the relocate the projected location onto the terrain surface, but that ends up being very complicated, since following the perpendicular may not land on the same triangle, which would lead to a whole spate of extra calculations. At high framerates, with a terrain that is not very steep, the error from following a plumb line (rather than a terrain normal) is minimal, however. Since even steep hills on a race track are not really steep in the grand scheme of things, this seems like an acceptable tradeoff. But the terrain designer needs to be aware that the terrain must not contain vertical surfaces, and ideally should not have any very steep surfaces on which driving is intended. There is an element of brute force in the terrain height calculation. All the triangles of the terrain are searched until a triangle directly over or under the projected location is found. This is potentially slow, and eventually, the terrain should be changed to be a series of tiles laid out in a regular rectangular grid, each containing the necessary triangles to create the complexity needed. Since it is a trivial to figure out which tile a point lies over or under, this would dramatically reduce the number of triangles to search, but would make terrain design more complex, since triangles spanning tiles would need to be subdivided. Nonetheless, for now there is one way the terrain designer can optimize this use of a single big terrain: since the triangles are searched in order, putting the triangles most likely to be driven on first in the list, and those least likely to be driven on last, will reduce the search time except for when the driver goes off course. Currently the car is controlled by a three-point connection to the terrain: the two front wheels (determining the roll angle) and a point between the rear wheels (determining the pitch angle). This means that the rear wheels may actually be above or below the terrain, but this is not noticeable given a non-steep terrain. Eventually, it would be nice to have a four point connection, with some reasonable logic to determine which of the four wheels at any point in time might be out of contact with the ground (since only three points can be guaranteed to be laid flat on an uneven surface), and perhaps with some suspension travel. Also, the car's location is currently relocated by moving the centre point between the two front wheels, and the rear wheels are just dragged along, without regard to whether they should have some influence on the direction. Finally, there is currently no mechanism to determine the characteristics (such as coefficient of friction or roughness) of the various parts of the terrain. 10. Throughout the in-code documentation there are various "Lazy Programmer" alerts. For example, there are several classes that are singletons (i.e. there should only be one instance, such as the keyboard), but there is no guarding code to ensure that. Another example is that in classes that are hard to duplicate, and for which there is no reason to duplicate, I have declared a copy constructor and assignment operator but never coded them. This is a lazy way to prevent copies: if I ever try I'll get a mysterious linker message saying it can't find the code for the copy constructor or assignment operator, rather than a more appropriate message or runtime error. A third lazy programmer example is that I frequently use fixed size arrays, rather than dynamically allocated memory, for much of the data stored in the classes. I, as a lazy programmer, figure that once the main logic is working well, it is easy enough to go later and change the fixed arrays to dynamic structures, rather than try to debug both basic program operation and the dynamic allocation at the same time. After all, why do today what you can put off until tomorrow, especially if the user won't know. 11. I needed to vary the pitch of the engine by manipulating the frequency at which the engine noise is played back, but I ran into a problem trying to raise the pitch above the original frequency. (The problem is that in a repeating sound, if the frequency goes above the original, the sound segment becomes shorter than the repeat interval, making gaps in the sound). So the engine noise should be recorded at the maximum desired pitch, with the program only varying it to pitches at or below that. 12. The code was originally written using DirectX, where matrices are stored in row major form, and vectors are handled as 1x3 matrices. All the OpenGL documentation uses column major form for matrices, and treats vectors as 3x1 matrices, with matrices therefore multiplied in the reverse order. Too late I realized that I probably could have stuck with row major form for almost all of the OpenGL stuff, and so my code has two ways of handling matrices. The upside is my OpenGL code matches descriptions in the OpenGL documentation. I must say, these two opposite-yet-the-same views of the mathematics is bad for a mild dyslexic like myslef. Similarly, DirectX uses a "left-handed" coordinate system (very sinister), where the z-axis goes into the screen, while OpenGL uses a right-handed system where the z-axis comes out of the screen. Again, too late I realized a simple transformation can probably handle most of the difference, so my code is instead littered with Z-coordinate reversal in the OpenGL code. OpenGL came first, so why did Microsoft have to do it differently? I guess it started when they changed / into \, just "because". Good luck playing with the source code. Let me know if anything good comes of it. Evan Alexander Weaver, 2 June 2004