练习OpenGL库的使用,代写一个3D模拟器。
Aims
- To implement an object-oriented program using C++.
- To make proper use of an industry standard 3D graphics library (OpenGL).
- To interface with a live stream of data to control a graphical object and display it in its environment.
Background
Modelling of custom mechanical, electrical and autonomous systems can be a
beneficial step to understanding a system. While CAD software provides a good
design tool, modelling of live systems specifically for control and status
monitoring can be more efficient under purpose built software rather than
modifying existing software to meet the system’s requirements. This assignment
will require you to extend the given 3D world modelling software to model the
motion of two or more ground vehicles. You will then be required to read data
streams or an input device in real-time, use the data to control the graphical
ground vehicles and display them in their environments.
Supplied Code Overview
You should begin this assignment by downloading the base code ZIP file from
Moodle. A number of files have been provided to allow you to view and navigate
a simple 3D world so you can test your code. See Section 4 for details on
downloading and setting up the given base code.
Once the downloaded code is up and running the list of controls in Table 1
will allow you control the vehicle and to move the virtual camera.
Control | Description |
---|---|
Arrow keys | Drive vehicle forward/left/backwards/right. |
W,A,S,D | Move camera forward/left/backward/right. |
C | Descend camera vertically. |
(space) | Ascend camera vertically. |
Mouse drag | Rotate the camera’s viewing direction. |
0 (zero) | Move the camera to the origin. |
P | Move the camera to vehicle pursuit position. |
Additionally, you can exit the program by pressing Escape. See the use of the | |
“KeyManager” class in the main source file for more information on how | |
keyboard events are linked to the virtual OpenGL Camera. In short, keys that | |
are pressed once (such as getting the virtual camera to move to the origin) | |
are handled the normal way via GLUT (OpenGL Utility Toolkit). To handle | |
multiple keys being held down at the same time, the additional functionality | |
of the custom “KeyManager” class is used. | |
Usually when modelling a vehicle’s pose and motion from live sensor data you | |
would acquire the data from a suite of on board sensors, a centralised | |
database or a network socket. For this assignment, you will be reading data | |
from a data server. | |
The “Shape” class provided should be used as the parent or base class for all | |
shapes outlined in Section 5.1. For simplicity, all shapes you create will | |
have a 3D position using (x, y, z) coordinates and a yaw angle in degrees. Yaw | |
is the rotation in the horizontal plane, which in this case is the XZ-plane. | |
Zero degrees of rotation means you are facing a direction parallel to the | |
positive x-axis. The vertical axis is the y-axis and we are using a right hand | |
coordinate system. | |
The “Vehicle” class should be used as the parent or base class for designing | |
vehicles as outlined in Section 5.2. The base vehicle object uses a basic | |
mathematical model to translate speed and steering values from input devices | |
such as the keyboard or an Xbox controller to motion. Your initial task is to | |
practice writing derived classes that represent different vehicle designs. See | |
Section 5.2 for further details. | |
After you have defined a custom class deriving from the Vehicle class you can | |
test it by finding the following section of code in the main() function in the | |
file main.cpp: |
// vehicle = new MyVehicle();
—|—
You should replace “MyVehicle” with the name of your custom vehicle class and
uncomment the line of code. This will enable the rest of the keyboard and
drawing functions to be linked with your custom vehicle class.
Setting Up
The following steps will get you up to compiling and running the base source
code provided on Moodle. There is a separate section for each of Windows,
Linux and Mac OS X. Although there are different steps for each operating
system, all of the base assignment code can be found in the AssignmentGL-
Base.zip file on Moodle. All supporting headers, libs and dlls can be found in
the AssignmentGL-Support.zip file on Moodle.
Windows
These steps are based on Visual Studio 2010, but should also work for later
Visual Studio versions. As a student of the University of New South Wales, you
are eligible to download a free student-licensed version of Visual Studio
Professional by visiting Microsoft Dreamspark (www.dreamspark.com) online.
Setting up a new project
- Open Visual Studio and create a new project.
- From the Templates panel on the left select Visual C++ and then Win32.
- Select Win32 Console Application.
- Enter a project Name. (For example “Assign2 “).
- Choose a Location to place the project.
- Leave the Solution Name the same as the project Name.
- Click OK.
- Click Next to go to the “Application Settings” page.
- Make sure Application Type is “Console Application”. Make sure Additional options has “Empty project” checked and “Precompiled header” and everything else unchecked. Make sure Add common header files for has every option unchecked.
- Click Finish to finish setting up the project.
Including the base source code
- Download the Assignment ZIP (AssignmentGL-Base.zip) file and extract to somewhere easy to find.
- Back in the new project you’ve just created find the Solution Explorer. If you cannot find the Solution Explorer go to the View menu then click Solution Explorer.
- A cpp file will have been created with the same name as the project, for example Assign2.cpp. Open this file and delete the contents (we will be using our own pre-defined main function).
- In the Solution Explorer right-click “Header Files”, select “Add” and then “Existing Item”.
- Locate the folder where you extracted the Assignment ZIP files. Highlight all the hpp files and click “Add”. (You can select a group of files by holding Control and clicking on each file).
- In the Solution Explorer right-click “Source Files”, select “Add” and then “Existing Item”.
- Locate the folder where you extracted the Assignment ZIP files. Highlight all the cpp files and click “Add”.
All the given header and source files are now set up and included correctly.
Setting up OpenGL headers, libs and dlls
- Download the AssignmentGL-Support.zip file from Moodle and extract the files to an easy to find location. In the Win32 folder there should be three folders: dlls, include and lib.
- Copy the include and lib folders to an easy to find location, for example C:\include and C:\lib.
- Open the dlls folder and copy the contents to C:\Windows\System32. Alternatively you can place the dll files in the same directory that the compiled exe file will eventually be compiled to. If you keep Visual Studio in “Debug” mode (this is the default mode), the exe will eventually be compiled to a “Debug” folder in your project files.
Compiling and Linking with OpenGL
- Back in the Visual Studio project open project properties (either via Alt+F7 or via the “Project” menu then “Assign2 Properties”).
- Navigate to the Configuration Properties, C/C++, General section.
- Set “Additional Include Directories” to the location where you placed the GL files include directory, for example C:\include.
- Navigate to the Configuration Properties, Linker, General section.
- Set “Additional Library Directories” to the location where you placed the GL files lib directory, for example C:\lib.
- Navigate to the Configuration Properties, Linker, Input section.
- On “Additional Dependencies” click the down arrow and the “Edit”.
- Add to the textbox, one per line: opengl32.lib, glu32.lib, and glut32.lib
- Click OK.
- Click OK.
Everything should be properly set up and ready to compile and run. Press F5.
This should compile and then run the program. If a window appears then it
worked. Try and move around the basic 3D world using the controls outlined
earlier in Table 1 of this document.
If it didn’t work look at the Output window, usually at the bottom of Visual
Studio. If the Output window is not visible you can select it from the “View”
menu. If you can self diagnose the problem that is great, but you are also
encouraged to post a topic on the Assignment Two Discussion Board in Moodle.
Linux and Mac
These steps are based on using the g++ compiler.
Linux and Mac OS X are relatively the same, except on most Linux distros the
compiler and libraries are installed by default. In Linux, if you try to run
the command g++ and the shell complains that it cannot find the command, you
should install the compiler by running the following command:
Ubuntu/Debian:
apt-get install build-essential
RedHat:
rpm install build-essential
On Mac OS X you will need to install Xcode. Xcode is available via the App
Store for free, but a link can also be found on http://developer.apple.com
if you have a free Apple developer licence.
Xcode is something like 3 or 4 GB, so be warned.
Setting up a folder structure
- Create a new directory called, for example, assign2.
- Download the AssignmentGL-Base.zip file and extract the hpp and cpp files to the newly created directory.
Setting up OpenGL headers and libs
On Mac OS X, downloading Xcode will automatically set up OpenGL, GLU and GLUT
in the correct place. On Linux you should be able to apt-get install
(Debian/Ubuntu) or rpm install (RedHat) the correct packages. If on Linux and
you can’t install the headers and libs using a packing service, use the
following backup steps:
- Download the AssignmentGL-Support.zip file from Moodle and extract the files to an easy to find temporary location. In the Linux folder there should be two folders: include and lib.
- Copy the include folder to a location that’s easy to find (for example: /include).
- Copy the lib folder to a location that’s easy to find (for example: /lib).
Please ask for assistance in the Assignment Two Discussion Board on Moodle if
you run into problems.
Compiling and linking with OpenGL
It is recommended to put the following text in a Makefile file in the
assignment code directory.
LIBS = -lGL -lGLU -lGLUT
SRC =
LIBDIR = -L/lib
INCDIR = -I/include
all:
—|—
On Mac OS X you may not need to specify the LIBDIR or INCDIR for OpenGL
support files as Xcode might have set them up properly for you. Also, Mac OS X
users might find it easier developing directly in Xcode instead of using g++
directly. There are plenty of tutorials online for setting up an OpenGL/GLUT
project with Xcode, but if you need further assistance, please ask in the
Assignment Two Discussion Board on Moodle.
By creating a Makefile you can simply type make at the command line in the
assignment directory and it will compile and link your code to from an
executable. Remember, when you add more cpp files make sure you make the
necessary changes to the Makefile as well.
You can then run the program by typing:
./run
Part 1 - Constructing Shapes and a Vehicle
Creating Basic 3D Shapes
Your first task is to extend the Shape class to implement a set of basic 3D
shapes. When we say extend we mean the object oriented programming term -
class derivation - rather than adding a large chunk of code to the Shape class
itself. The Shape class contains position and orientation attributes, which
are common to every 3D object. You need to implement classes at least for the
following shapes outlined in this document. For simplicity, each object can be
assigned one colour using three floats to specify the red, green and blue
components of the colour. Look at the “Shape.hpp” file for more details on how
to interface with and extend the Shape object.
Rectangular Prism
This shape should make use of three additional member attributes concerning
the length of the shape in the three spatial dimensions (for example, x
length, y length and z length). You should also devise how the volume of the
prism is positioned relative to it’s internal x, y and z attributes and how
the rotation variable rotates the object in the horizontal plane. One
suggestion could be to define the (x, y, z) location as the center of the
object and the object’s rotation is applied about the origin of the object.
Triangular Prism
This shape should make use of additional member attributes to specify the
dimensions of the triangular prism, the choice of which is left up to you. The
length of the prism is an obvious choice for one of these attributes, but you
should determine a method for specifying the shape of the triangular
dimensions of the object. For example, two different approaches, among others,
are to store:
- the three side lengths of the triangle, or
- two side lengths and an angle.
You should also remember to intelligently decide on a center for your object
and how this relates to object rotation.
Trapezoidal Prism
You should decide on additional member attributes to specify the shape of this
object. In particular you need to decide on a way of specifying the dimensions
of the trapezium at the end of the prism in an efficient way.
Cylinder
You can use additional member attributes such as length or height along with
radius to specify the dimensions of a cylinder. You may make use of the
inbuilt “gluCylinder” or “glutCylinder” functions. A cylinder for this project
must be a solid cylinder. That is, it should consist of a curved surface
together with two circles on each end of the cylinder. The center of a
cylinder might be defined as the point half way along the central axis of the
cylinder for rotation purposes.
Vehicle Modelling
Using the collection of basic 3D shapes we have defined in the previous
section, you should implement some models of different types of vehicles. When
implementing a model of a vehicle you should extend from the base “Vehicle”
class provided with the initial collection of files. When constructing the
model from basic shapes you should position shapes in the vehicle’s local
frame of reference. As a result, the “draw” function in your custom vehicle
class should contain the following general structure:
void MyVehicle::draw()
{
// move to the vehicle’s local frame of reference
glPushMatrix();
positionInGL();
// all the local drawing code
// move back to global frame of reference
glPopMatrix();
}
—|—
The base “Vehicle” class contains an “update” function which handles
interpreting control input into vehicle motion using a basic mathematical
model. Although it is not required, you may modify or improve this
mathematical model in the “Vehicle” class. The “update” function, combined
with the drawing structure above will position and orient the vehicle’s model
correctly in 3D space. You need to make sure you position shapes so that the
vehicle is facing the positive x-axis in it’s local frame of reference. For
best results make your vehicles not longer than 4 units and not wider than 3
units.
However, if your vehicle has wheels (which it probably will), you must make
the wheels rotate at a rate that approximately resembles the vehicle’s speed.
Additionally, if your vehicle has wheels that can steer when steering then you
also should make the models wheel’s steer proportional to the steering angle.
When your work is being assessed, we will give you a model of vehicle
(hereafter called the local vehicle) that contain both wheels that rotate and
wheels that steer. In addition you will also receive one or more vehicle
models (here after called remote vehicles) from a data server. You need to
display all these vehicles on the screen. See Section 8 for more details.
As good preparation, you could implement some prefabricated vehicle parts to
make constructing vehicle models quicker. For example, a “Wheel” class could
be created that contains a cylinder wheel with either cylindrical or prism
based spokes for the wheels to tell they are turning when the vehicle is in
motion. The more prepared you are beforehand, the easier the assessment
process will be.
Final Checklist for Part 1
- Create a RectangularPrism class extended from Shape class.
- Create a TriangularPrism class extended from Shape class.
- Create a TrapezodialPrism class extended from Shape class.
- Create a Cylinder class extended from Shape class.
Get your first progress check done by any demonstrator before 5.00 pm of
Friday week 7 showing above four items in the form of source code or on screen
functionality. - Instantiate local/remote vehicles extended from Vehicle class.
- Vehicles should have wheels that roll when driving forward/backward.
- Vehicles should have front wheels that steer when steering.
Get your second progress check done by any demonstrator before 5.00 pm Friday
of week 8 showing above three items in the form of on screen functionality.
Part 2 - Using Data from the Data Server
This part deals with dynamically adding vehicles into your program using a
server, and making these remote vehicles move. (See Section 7 to see how to
make the local vehicle move). In order to do this you will need to interface
with a data source over an Ethernet/Wi-fi connection. We have provided a class
(called RemoteDataManager) and a set of functions (declared in Messages.hpp)
that hides away most of the low-level implementation detail for you. The
RemoteDataManager class will connect over the internet to the UNSW robotics
server www.robotics.unsw.edu.au. Communication with the server is
bidirectional, and is used to synchronise your environment with the server’s.
To enable communication with the data server, uncomment the relevant line of
code in the idle() function in the main.cpp file near this line.
Most of the messages received from the data server will be handled by the
provided code, with the exception of the “M”-vehicle model message. The “M”
message contains one or more VehicleModel objects represented according to the
data structures given in Table 2, where each VehicleModel has a vector of
shapes. You will need to process each model object to instantiate the remote
vehicles correctly, and in each model object, you will need to process the
shape information. The relevant section of code can be found in the main()
function in the file main.cpp near this line:
//otherVehicles[vm.remoteID] = new MyVehicle();
—|—
You should replace “MyVehicle” with the name of your custom vehicle class and
uncomment the line of code. This will enable remote vehicles to be added to
the map of other vehicles to be drawn and updated.
As can be seen, the information here is not any different to the information
you used to draw the local vehicle. Hence, you can use the parts of software
you used to draw the local vehicle to draw these remote vehicles in an
identical manner provided you match up the data received from the data server
to your own vehicle data representation. As soon as you have drawn your remote
vehicles, they will begin to move according to the state data streaming from
the data server. The part that moves the remote vehicles has already been
completed for you and you therefore do not have to do this part. However, on
connection you will need to tell the remote server what your vehicle looks
like. The functionality to package and send the data has been provided by the
RemoteDataManager networking software. You need to provide the code to fill in
the outgoing data structure, given in Table 2. If the data you provide is
beyond nominal range, the server may respond with an error message and
terminate your connection.
enum ShapeType
{
UNKNOWN_SHAPE,
RECTANGULAR_PRISM,
TRIANGULAR_PRISM,
TRAPEZOIDAL_PRISM,
CYLINDER
};
union ShapeParameter
{
struct RectangularParameters
{
float xlen; // length along x-axis
float ylen; // length along y-axis
float zlen; // length along z-axis
} rect;
struct TriangularParameters
{
float alen; // length of side A (bottom)
float blen; // length of side B (left)
float angle; // angle (degrees) between side A and B
float depth; // length along z-axis
} tri;
struct TrapezoidalParameters
{
float alen; // length of side A (bottom)
float blen; // length of side B (top)
float height; // distance between side A and B
float aoff; // distance A is shifted from B by, from the left
float depth; // length along z-axis
} trap;
struct CylinderParameters
{
float radius;
float depth; // length along z-axis
bool isRolling; // needs to roll with vehicle?
bool isSteering;// needs to steer with vehicle?
} cyl;
};
struct ShapeInit
{
ShapeType type;
ShapeParameter params;
float xyz[3];
float rotation;
float rgb[3];
};
struct VehicleModel
{
int remoteID;
std::vector
};
struct VehicleState
{
int remoteID; // this should be 0 for local vehicles
float x;
float z;
float rotation;
float speed;
float steering;
};
—|—
Final Checklist for Part 2
- Implement code in your graphical application to receive data server message “M” and display one or more vehicles accordingly with the correct shapes.
- Implement code in your graphical application to report your local vehicle status to the server.
Part 3 - Additional Graphical Objects
This part requires preparation before you come to the assessment. Learn to
draw additional graphical objects around your graphical vehicle. This could be
to draw in 3-D a line loop forming an arrow pointing at your vehicle from its
front, rear, left or right and moving with the vehicle as you move the vehicle
using a game controller or the keyboard. It could also be a circle drawn in 3D
space around your vehicle and the circle should move with the vehicle. Other
examples are drawing an arrow of a shape specified by your assessor to
indicate the direction of travel as determined by steering. Above mentioned
examples do not form the full list of potential tasks you may get on the day
of the assessment. However, it is prudent to prepare for rendering graphical
objects that could move with the vehicle, its wheels or its steering.
Vehicle speeds will range from 0 to 10 ms and steering angles will range
between 15 and +15 degrees.
Final Checklist for Part 3
- Practice coding to incorporate additional graphical objects in 3D (which are not part of the vehicle body) that move with the vehicle.
- Additional graphical objects drawn around your vehicle must appear when the key ‘Z’ is pressed.
Assessment
- You are required to prepare a set of 3D objects and practice constructing vehicles from the objects in your own time.
- Complete first progress check by Friday 5 pm of week 7. First progress check does not earn you any marks. However, if late, you will lose one mark per calendar day. Unavailability of demonstrators closer to the deadline is not considered as an excuse. Please make sure you sign the demonstrator’s name sheet after a successful progress check.
- Complete second progress check by Friday 5 pm of week 8. You must have completed the first progress check to be able to complete the second progress check. Second progress check does not earn you any marks. However, if late, you will lose one mark per calendar day. Unavailability of demonstrators closer to the deadline is not considered as an excuse. Please make sure you sign the demonstrator’s name sheet after a successful progress check.
- You must make an appointment with the demonstrator assigned to you by Friday 5 pm of Week 8. If you miss this deadline, the penalty is one mark per calendar day. DO THIS WELL AHEAD OF TIME.
- You must get your work marked by a demonstrator during Week 10. You must have completed the second progress check to be able to get your work assessed. The penalty for late assessment is as per the School policy.
- During assessment you will be required to bring in all of your source code.
- At the time of assessment your assessor will give you the specifications for the local vehicle which you will then need to model using your developed classes. You will be given 20 minutes to prepare this model, complete any other assigned tasks and test them in your program. You will have incorporated all other functionality required before arriving at the assessment. As such when you connect to the data server, we should see the remote vehicles on the screen and they should begin to move automatically.
- After such time your demonstrator will check the model you created to see if it appears to be correct to specification and any moving parts properly animate under vehicle motion.
- Your demonstrator will also check the reporting of your local vehicle status to the data server.
- You will then be asked to demonstrate your program by driving your graphical around on the screen.
- Your demonstrator will then ask you to explain your code and class structure. Marks will be awarded out of 20 according to the following scheme:
* (a) Making the program compile and be fully operational on the day of assessment.
* (b) Whether the model looks and animates correctly.
* (c) The vehicle motion is demonstrated using an XBox 360 game controller.
* (d) Whether the live feed of data from the data server is correctly read and used.
* (e) Whether the local vehicle’s status is correctly reported to the data server.
* (f) Modularity: is the program broken up into well defined files, classes, functions?
* (g) Structure: Is the code efficient and logical?.
* (h) Did you clearly explain the program to your demonstrator by clearly answering the five questions he or she will ask you?
* (i) If you are up for a challenge, demonstrate to your demonstrator how you could give chase to vehicle with vehicle ID 1, when you press ‘L’. You will get an additional 10% of your mark, however, the total bonus marks for all assignments may not exceed 3.
An Example Vehicle to Model
Below is a sample specification for a simple vehicle.
- The x-axis, y-axis and z-axis are shown by the red, green and blue vectors respectively.
- The small front wheels should be turned to reflect the vehicle’s steering angle.
- All four wheels should rotate relative to vehicle speed.
- The main body should be coloured with the RGB values (0.0, 0.6, 0.0).
- The front (small) wheels should be coloured with the RGB values (1.0, 0.0, 0.0).
- The back wheels should be coloured with the RGB values (0.0, 0.0, 1.0).