Graphics Programming in C++ With G3D

download Graphics Programming in C++ With G3D

of 17

Transcript of Graphics Programming in C++ With G3D

  • 8/8/2019 Graphics Programming in C++ With G3D

    1/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    1/17

    Graphics Programming in C++ with G3D

    C++ is the most widely used programming language for computer graphics in both

    industry and research. It combines low-level features like direct memory manipulationwith high-level features like exceptions, classes, and macros. These make C++ incredibly

    powerful and adaptable, but also create pitfalls for new programmers.

    G3D is an open source, cross-platform library for creating interactive 3D programs like

    games. 3D applications, such as games, medical software, artist tools, and film rendering

    software are incredibly complex. Nobody writes them from scratch because it takes about

    100,000 lines of infrastructure code before you are even ready to begin the programming

    that is specific to your project. There are several large libraries (engines) that are

    regularly used in research and the software industry to support 3D graphics, including

    OGRE, Source, OpenCV, and Unreal. G3D is the one you will use as the primary support

    library for your projects in this course. It contains OpenGL, libpng, libjpg, zlib, ffmpeg,

    and other popular graphics libraries, and extends them to be both easier to use and more

    powerful.

    This document is a quick-start guide for programmers with Java experience but little or

    no C++ and graphics experience. Some additional resources are:

    G3D Manual, /usr/local/371/html/index.html iCompile Manual, /usr/local/371/html/icompile-manual.html Lerner, C++ for Java Programmers,

    http://www.cs.williams.edu/~lenhart/courses/cs371/c++forjava.html

    Fiamingo, DeBula, and Condron, Introduction to Unixhttp://8help.osu.edu/wks/unix_course/intro-1.html

    Kernighan and Ritchie, The C Programming Language Stroustrup, The C++ Programming LanguageNeider, Davis, and Woo, OpenGL Programming Guide (also available online at

    http://fly.cc.fer.hr/~unreal/theredbook/ )

    1 Critical Things You Need To Know About C++

    1. C++ variables are objects by default. Java variables are pointers. In C++ youhave to explicitly declare a pointer if you want one. Usually you can use a

    reference (e.g., Vector3&) or a reference-counted pointer (e.g., TextureRef)

    instead to avoid some of the dangers of C++ pointers.

  • 8/8/2019 Graphics Programming in C++ With G3D

    2/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    2/17

    2. C++ lets you control whether objects are allocated on the stack or the heap. Youhave to manually delete objects in the heap when you are done with them, which

    is error prone, so try to allocate on the stack whenever possible.

    3. C++ has separate static compilation and linking steps. Linker errors usually occur because you forgot to implement a method (possibly due to misspelling) or

    because your .icompile file isnt configured correctly.

    4. Put a semi-colon at the end of class declarations.5. Objectassignment copies the whole object (which might be really slow).

    Matrix4 A;Matrix4 B;Matrix4& C(A); // Alias for AMatrix4 D = A; // Copy of AC[0][2] = 1.0f; // Also changes AD[0][2] = 7.0f; // Leaves A (and C) unchanged

    6. Stack declarations accept constructor arguments. Use them instead of copying!// GoodArray a(1024*1024);

    // Bad: copies 4 MB of memory!Array a = Array(1024*1024);

    7. Dont use:a. C array declaration syntax, e.g., int x[10] (use Array instead)b. Multiple inheritancec. Pointer arithmeticd. Multiple object declaration (e.g., dont write Vector3 a, b, c; put each on

    its own line instead.)

    e. C memory management: malloc, calloc, free (use stack allocation ornew)

    f. C++ new and delete, if you can avoid them with stack allocationg. std:: data structures when a G3D:: one is available (use Array, Table, Set,

    Queue instead)

    h. C strings (char*) except for initialization (use std::string instead)8. Pass large objects by reference using &:

    int computeSum(const Array& array);

    9. Never return a reference or a pointer to an object on the stack from a method.10.Make arguments (especially references) and methods const whenever possible11.NULL is in all caps. It is equal to 0.12.The Boolean data type is bool (not Bool, Boolean, BOOL, or anything else). It

    requires 4 bytes on most machines.

  • 8/8/2019 Graphics Programming in C++ With G3D

    3/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    3/17

    13.Primitive types, including int, float, bool, and pointers, are uninitializedwhendeclared as locals or member variables (they are not zero/NULL like in Java.)

    14.onGraphics() runs 30 times per second or more to redraw the screen. Dont doanything slow from it, like running a ray tracer or loading a file from disk.

    15.Put class declarations only in .h files16.Put class implementations only in .cpp files17.Wrap every .h file in a header guard. E.g., for Foo.h:

    #ifndef Foo_h#define Foo_h

    #endif Foo_h

    2 Compiling Code

    Programs written in any language pass through two steps before they are run. The first

    step is compilation, which checks syntax and types and then produces an object file (in

    Java, these end in .class; in C++ they end in .o or .obj). The second step is linking, which

    connects all of the object files into a single program. In Java, the linking step happens at

    run time and is not explicitly seen by the programmer. In C++, the linking step is

    performed in two parts: explicitly after compilation (static linking) and at run time

    (dynamic linking). Knowing how to invoke the compiler and linker is a valuable skill. It

    is also one that takes some time to acquire. To help you get started quickly, Ive provided

    a program called iCompile that will automatically compile and link your programs. It

    will also provide some additional functions if you request them, like copying data files

    from your source directory to your build directory and creating documentation for your

    code.

    To learn more about compiling and linking you can run iCompile with the verbosity

    3 argument. All of the green text that it prints contains the actual commands being

    issued to the compiler. You can use man gcc to see a description of those commands.

    3 Debugging

    The GNU debugger, gdb, is a command-line program that you can use to pause a running program, look at its variables, and see a stack trace. You can run your program under

    gdb by specifying the gdb flag instead of the run flag to iCompile. Compared to

    a visual debugger, gdb is unfortunately hard to use and many programmers use simple

    print statements and clever rendering tricks to try and diagnose problems in their

    programs. There are some situations that are impractical to debug by those methods,

    however, and in those situations gdb is essential. The most common of these is when an

    assertion failure occurs and you need a call stack to locate the source of the problem in

    your program (the assertion failure typically contains a line number inside a library).

    When this happens, select (3) Debug from the G3D assertion prompt, which will leave

    you at the gdb prompt. Type bt to see a backtrace (call stack), which will identify the

    line of your code that triggered the failure. Type help in gdb to learn more about other

    commands, e.g., for looking at local variables.

  • 8/8/2019 Graphics Programming in C++ With G3D

    4/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    4/17

    G3D creates a log.txt file every time your program is run. This contains information that

    is useful in debugging, including the last assertion message and a list of all files that your

    program accessed.

    G3D provides several routines to aid in debugging your programs. The names of these

    routines all begin with debug.

    The debugPrintf function is like printf except that it disappears when you make an

    optimized build of your program and works in a useful way on all operating systems.

    The debugBreak macro pauses execution. It is a way of programmatically setting a

    breakpoint for the debugger.

    The debugAssert and debugAssertM macros execute a debugBreak if the specified

    condition is not true. A command-line prompt then allows you to see more information

    about the error and choose how you would like to continue. Sometimes you want an

    assertion to remain in an optimized build. Use alwaysAssertM in that case.

    Three routines output debugging text. screenPrintf displays information directly on your3D window. It is most useful for debugging interactive programs that continuously

    rendering. debugPrintf prints to the xterm that created your program. logPrintf prints to

    the log.txt file, which is useful if your program is crashing in a way that takes out the

    xterm (or the computer).

    Later in the semester when using OpenGL in more advanced ways, the getOpenGLState

    function allows you to see the current values of the OpenGL registers.

    G3D makes it really easy to create GUI controls to adjust parameters in your program,

    which can be very helpful for debugging. Commands like:

    debugPane->addCheckBox(Visible, &visible);

    let you map variables from your program directly to controls on the screen so that you

    can try many variations without recompiling your program.

  • 8/8/2019 Graphics Programming in C++ With G3D

    5/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    5/17

    4 C++ Declarations

    4.1 C++ Syntax

    public class A : public B {private:

    float x;

    public:

    A();

    ~A();

    void foo();};

    4.2 Declarations

    The class is split across two files. The declaration is in a header (.h) file. This contains all

    of the information needed to tell the compiler how big the class is, what methods it has,and what members it has. The implementation is in a .cpp file; this is what tells the

    compiler what the methods do. The #include statement effectively pastes one file into

    another. Classes must be declared before they are used in C++. This is why we have

    header filesthey enable us to paste the declaration before any use of the class, even if

    the implementations of two classes depend on each other.

    When the declarations of two classes depend on each other, C++ uses a forward

    declaration:

    // forward declarationclass B;

    class A {public:

    void add(B& b);};

    class B {public:

    void add(A& a);};

    Compilers need to know how big objects are. Since all pointers are the same size, the

    compiler knows how big B* and B& are (and the special case of a B as a return value),

    even if they dont know how big an instance of B is. The reason for the forward

    declaration is to assure the compiler that you didnt type B by accident. The forwarddeclaration how you promise the compiler that a declaration of B will show up at some

    point in the future (if one doesnt, then the linker will complain).

    4.3 Functions

    C++ allows the definition of functions, which are like static methods without an

    associated class. You probably wont have to declare many of your own functions, but

    A is a subclass of B

    The following declarations are all privatemembers & methods.

    The following declarations are all public

    members & methods.

    Destructor, the opposite of a constructor.

    This runs when an instance is deallocated.

    Note the semi-colon

  • 8/8/2019 Graphics Programming in C++ With G3D

    6/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    6/17

    you likely will have to call functions written by others. For example, endsWith is a

    function in the following code snippet:

    if (! endsWith(filename, /)) {filename += /;

    }

    5 Constants

    The order of initialization of global constants is undefined in C++. For example, this

    means that the following code could produce ANY value for the perimeter of the unit

    circle, becausePImight be initialized afterunit.

    const float PI = 3.1415926;

    class Circle {public:

    float perimenter;Circle(float radius);

    };

    Circle::Circle(float radius) : perimeter(PI * 2.0f * radius) {}

    Circle unit(1.0f);

    There are several tricks to avoid being caught by this behavior. For integer constants, the

    enum type is always created at compile time. So integer constants can be expressed using

    (the admittedly awkward syntax):

    enum {DAYS_IN_WEEK = 7};

    Macros can be used for any kind of constant:

    #define PI (3.1415926)#define DAYS_IN_WEEK (7)

    For class constants, it is necessary to create an inline function that returns a static const

    variable. This can also be used for other data types:

    inline const Matrix3& rot45() {static const Matrix3 M =

    Matrix3::fromAxisAngle(Vector3::unitX(),toRadians(45));

    return M;

    }

    That example uses a number of C++ features too complicated to explain here, but is a

    pattern that you can follow for creating constants and static class member constants that

    are guaranteed to be valid at the global level. Constants inside methods and functions do

    not require such care and can be defined as if they were variables.

  • 8/8/2019 Graphics Programming in C++ With G3D

    7/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    7/17

    6 Inheritance

    Inheritance in C++ is tricky. In general, never use multiple inheritanceit is especially

    confusing and can produce code that compiles but does something other than what you

    expected.

    In Java, every class can be subclassed and every method overridden unless explicitlymarked with final. C++ has the opposite convention. Unless a method is explicitly

    marked as virtual, it cannot be overridden (well, it can be overridden, but the result

    wont be what you wanted.)

    In addition, unless a class has a destructor that is declared as virtual, it may also leak

    memory when instances are destroyed. The G3D classes that you need to subclass are

    declared with virtual destructors and appropriate methods (e.g., event handlers) declared

    virtual. The G3D classes that you dont need to but might like to subclass (e.g., Vector3)

    are not declared virtual and your program will be in error if you try to inherit from them.

    A pure virtual method is one that is not implemented by the base class (Java calls this

    abstract). Pure virtual methods declarations have =0 at the end, like:

    virtual void intersect(const Ray& worldRay,float& hitDistance, class Hit& hitResult) = 0;

    See http://blogs.msdn.com/oldnewthing/archive/2004/05/07/127826.aspx for a discussion

    of inheritance.

    7 Factory Methods

    C++ provides overloaded constructors so that a class may be instantiated in multiple

    ways. Sometimes the constructors would be confusing if they were distinguished only by

    overloading. In theses cases it is common practice to provide a static method on the classthat produces the desired instance. These so-called factory methods are also used for

    alternative memory allocation schemes like buffer pooling. Here are examples of using

    factory methods:

    Vector3 up = Vector3::unitY();Matrix3 rot = Matrix3::fromAxisAngle(Vector3::unitX(),

    toRadians(15);

    8 Manual Memory Management

    Unlike Java, in C and C++ programmers must both allocate (using new or malloc)

    memory for objects and deallocate that memory (using delete or free) when the

    object is no longer in use. Forgetting to deallocate memory leads to memory leaks,which will eventually make a program run slowly and crash. Deallocating memory that

    is still in use creates a dangling pointer and will likely cause a program to crash. As

    you can imagine, this manual memory management is one of the trickiest parts of C++

    programming and is the source many errors.

    Fortunately, G3D helps you avoid ever having to manually manage memory. You should

    almost never have to write new or delete in your program, in fact. Two tools that

    G3D provides are common data structures and reference counting.

  • 8/8/2019 Graphics Programming in C++ With G3D

    8/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    8/17

    9 Data Structures

    The G3D Array, Queue, Set, and Table data structures are all dynamic. This means that

    you just declare variables of these types, for example,

    Array myIntegerArray;

    and G3D will handle allocating and deallocating the underlying memory that is used to

    represent the data structure. C++ also provides a standard library with arrays and queues,

    and the C language provides built-in arrays (like Javas built-in arrays). You should

    generally not use those in this class. The G3D versions are better optimized for graphics,

    are more platform independent, and are easier to use.

    For string processing, use the built-in std::string class. This can be initialized from a C-

    string (char*), but is a class with useful methods. To convert std::string to C-string, use

    the std::string::c_str method. An example of using std::string is:

    std::string name = McGuire;debugPrintf(Length: %d\n, name.size());debugPrintf(Value: \%s\\n), name.c_str();

    if (name == Murtagh) {debugPrintf(username = tom\n);

    } else {name += Username Unknown!);

    }

    Note that words in quotes are not strings; they are values that can become strings. So

    hello + there does not concatenate strings, but std::string(hello) + std::string(

    there) does.

    10 Reference Counting

    Reference counting is a form of automatic memory management (also known as garbage

    collection). Most G3D classes, like Texture, GFont, and Shader, are managed with

    reference counting. For these classes, declare your instance to be the corresponding ref

    type (ref means reference, another word for pointer). Use the appropriate factory

    method to create an instance of this class. For example:

    TextureRef tex = Texture::fromFile(apple.jpg);

    The ref classes act like regular C++ pointers in almost every way. They can be shared

    (aliased), set to NULL, and tested for equality. Use the -> or * syntax to dereference a

    pointer. Testing for NULL has a special syntax. Examples:

  • 8/8/2019 Graphics Programming in C++ With G3D

    9/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    9/17

    int sumWidth(TextureRef tex1, TextureRef tex2) {if (tex1.notNull()) {if (tex1 == tex2) {

    return 2 * tex1->texelWidth();} else {

    return tex1->texelWidth() +tex2->texelWidth();

    }} else {return 0;

    }}

    Never use C++ pointer syntax with these reference counted classes, e.g. DO NOT write

    Texture*. Never call delete or new with a ref class. You can make your own reference

    counted classes; see the G3D documentation for more information.

    11 Variable Initialization

    C++ does not initialize variables unless they are of a class with a default constructor. This

    means that most member variables and stack variables have undefined values. You

    should always initialize these immediately after declaring them or in your constructor.

    When writing your own class constructors, a special syntax allows you to initialize

    members before the constructor executes (this is equivalent to Javas member assignment

    during definition). The syntax is a colon after the constructor signature followed by a

    comma-separated list of members, each with its own constructor arguments in

    parentheses:

    class Player {public:

    Vector2 position;std::string name;Player();

    };

    Player::Player() : position(10, 2), name(Harvey) {}

    12 Pass By Reference

    In C++, a type written without a * declares a variable to be the actual object, not a

    pointer to the object as is the case in Java. Since you have been instructed not to use

    pointers in most cases, this means that almost all of your variables will be stack allocated

    objects or members defined inside another object.

    When assigning one object to another, a copy occurs. For a large object like GImage or

    an Array, this is potentially slow and is probably not desirable. This means that you willusually want to pass values into methods and functions by reference to avoid the copy. In

    C++, an ampersand (&) placed after a type makes it into a reference type that acts like a

    pointer but cannot be reassigned. Passing an object to a reference incurs no copy.

    For example, the following code passes a GImage by reference. Note the use of the const

    keyword to specify that the function cannot change.

  • 8/8/2019 Graphics Programming in C++ With G3D

    10/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    10/17

    int findRedPixelX(const GImage& image, int y) {for (int x = 0; x < image.width; ++x) {

    if (image.pixel3(x, y).r == 0) {return x;

    }}return -1;

    }GImage image(logo.gif);int x = findRedPixelX(im, 0);

    Note that the int was not passed as a reference in this example. That is because small

    data types like int are the same size as a pointer, so theres no performance advantage to

    passing them by reference. It is possible to pass a reference to an int, and in fact that is a

    convenient way of returning more than one value from a method. The following example

    demonstrates two values returned by passing them by reference.

    void findRedPixel(const GImage& image, int& x, int& y) {

    x = -1;y = -1;for (y = 0; y < image.height; ++y) {

    for (x = 0; x < image.width; ++x) {if (image.pixel3(x, y).r == 0) {

    return;}

    }}

    }GImage image(logo.gif);int x;int y;findRedPixel(im, x, y);

    13 OpenGL Register Model

    OpenGL is the platform independent library that allows access to graphics cards. It

    presents a virtual interface to the card called a Hardware Abstraction Layer (HAL). This

    abstraction layer refers to registers and other low-level hardware features. Graphics cards

    are generally operated by setting state (values in the registers) and then executing a

    function that uses that state.

    G3D follows this model. The RenderDevice class contains many set methods that

    assign values to OpenGL registers. Only two methods trigger actual rendering. These are

    RenderDevice::sendVertex and RenderDevice::sendIndices. Because graphics cards are

    designed as pipelines, sending a value means sending it down the pipeline to

    processing.

    Most bugs in OpenGL programs are from having the wrong state set when sending data.

    RenderDevice::push and RenderDevice::pop allow you to easily save and restore all of

    the state in the rendering system, which can be used to isolate one piece of code from

    another and reduce the number of state-based errors.

  • 8/8/2019 Graphics Programming in C++ With G3D

    11/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    11/17

    14 Color Model

    To a graphics card, 0 is the darkest value that can be displayed and 1 is the brightest.

    These values between 0 and 1 are often rescaled and stored in bytes as values between 0

    and 255. A color is represented by a triplet of red, green, and blue intensities (always

    written in that order). It can also be written as the hexadecimal representation of the

    bytes in such a triple, e.g. 0xFF8800 is redish yellow because it contains bright red, dimgreen, and no blue. The Color3 and Color3uint8 classes represent colors in G3D.

    In addition to red, green, and blue, an alpha channel is used to represent translucency

    and partial occlusion in many situations. An alpha value of 0 generally means

    completely translucent and a value of 1 represents completely opaque. Color4 and

    Color4uint8 extend Color3 with an alpha value. The alpha value is represented as the

    high-order bits when writing in hex: 0xFF000000 is opaque black.

    15 2D Coordinate System

    For historical reasons, in 2D (0, 0) is the upper-left corner of an image or the screen. The

    x-axis increases to the right and the y-axis increases down. This is used for

    RenderDevice push2D rendering, Texture coordinates, and for GImage pixel addressing.

    Texture coordinates vary from (0, 0) in the upper-left to (1, 1) in the lower right, while

    RenderDevice push2D, Image, and GImage use integer pixel coordinates.

    16 Immediate vs. Retained Mode Graphics

    In a retained mode graphics API, you create objects and they persist until destroyed,

    drawing themselves on screen and interacting with the user. An example is the Swing

    JComponent API in Java, where you create objects like JButton and JTextArea that then

    appear on screen until dismissed.

    In an immediate mode graphics API, you explicitly draw on a canvas that is periodically

    erased. There are no persistent objects; if the canvas is erased by your code or by anotherwindow drawing over it, your image will be lost forever and must be redrawn from

    scratch. An example of this is the Swing Graphics2D API used in the Component.paint

    methods.

    G3D provides pieces for writing retained mode code, but it is intended primarily for

    immediate mode rendering. In GApplet, the onGraphics event handler that you

    implement will be invoked several times a second. It will use calls on a RenderDevice to

    create an image that lasts only until the next frame of animation.

    17 Starting Your Program

    iCompile will automatically copy the contents of the data-files directory into the build

    directory, change into the build directory, and then launch your program. At that point,

    the main() function begins. Main must have the following syntax:

    int main(int argc, char** argv) {return ;

    }

  • 8/8/2019 Graphics Programming in C++ With G3D

    12/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    12/17

    Integer argc is the number of command line arguments that were passed to your program

    as strings. There is always at least one, which is the name of your program. Variable argv

    is a pointer to a pointer to a character. In C (not C++) syntax, it can also be viewed as an

    array of arrays, or an array of strings. Thats because a C array is just a pointer to the first

    element in the array. C doesnt keep track of how many elements are in an array for

    youif you go off the end, you simply start corrupting memory and the program crashes!

    Thats why argc is provided. A string in C (as opposed to a C++ std::string) is just anarray of characters. The last character is \0 (this is a null-terminated string), which is

    how you know when the string has ended.

    An example of simple command line argument processing is:

    int main(int argc, char** argv) {if ((argc > 1) && (std::string(hello) == argv[1])) {

    }return ;

    }

    Here, constructing a C++ std::string lets us use the == operator to tell if the two stringsare identical. If we just said hello == argv[1], then wed be testing if the memory

    pointer to hello was the same as the memory pointer argv[1], not asking if they

    contained equivalent strings. If youve programmed in Java, then you might recall the

    difference between String.equals and ==; in C++, thats the difference between

    std::string::operator== and C-string == on pointers.

    18 C++ Syntax Reference

    18.1 Pointers and References

    Object:Vector3 v;

    Reference: this acts like an object but re-uses the memory allocated for v and can never

    be assigned to different memory.

    Vector3& r(v);

    Pointer: this stores the address of the memory for v. It can be reassigned to a different

    address (or to NULL).

    Vector3* p(&v);

  • 8/8/2019 Graphics Programming in C++ With G3D

    13/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    13/17

    Reference counted pointer: this acts like a pointer, but garbage collects the object when it

    is no longer used. These are only available for some objects:

    TextureRef tex = Texture::fromFile(hello.jpg);

    Assign to a member:

    v.x = 1.0f;r.x = 3.0f;p->x = 7.0f;*p.x = 8.0f;tex->invertY = true;*tex.invertY = false;

    Read a member/invoke a method

    float f;f = v.y;v.unitize();f = r.y;f = p->y;f = *p.y;f = tex->width();f = *tex.width();

    Copy the value:

    v = *p;*p = v;r = v;

    v = r;*p = r;

    Point at the same value:

    p = &r;p = &v;Vector3* q(p);Vector3* s(&r);Vector3& z(*p);Vector3& c(r);q = p;

    18.2 Allocation

    Allocate on the stack (will automatically be deallocated when it goes out of scope):

    Vector3 v(5, 2, -15);

  • 8/8/2019 Graphics Programming in C++ With G3D

    14/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    14/17

    Allocate a C++ array on the stack (will automatically be deallocated when it goes out of

    scope), where the contents of the array are in the heap:

    Array v(10); // G3D; recommendedstd::vector x(10); // std; not recommended for 371

    Allocate a C array on the stack, where the contents of the array are also on the stack

    (generally undesirable):

    Vector3 v[10];

    Allocate a reference-counted object on the heap (will automatically be deallocated when

    the last pointer is dropped, just like in Java):

    Image3Ref im = Image3::createEmpty(100, 100);

    Allocate a non-reference counted object on the heap (avoid this whenever possible, since

    you must explicitly deallocate it or memory will leak!):

    MyObject* obj = new MyObject();delete obj;obj = NULL;

    18.3 static

    Declare that a method is on a class, not a member (same as in Java):

    class Ball {public:

    static BallRef create(const std::string& name);};

    In the .cpp file for the above example, do not put static in front of the implementation:

    BallRef Ball::create(const std::string& name) {return new Ball();

    }

    Declare that a member variable belongs to a class, not a member (same as in Java):

    class Cube {

    public:static int numCubes;

    };

    In the .cpp file for the above example, numCubes is initialized as:

    int Cube::numCubes = 0;

  • 8/8/2019 Graphics Programming in C++ With G3D

    15/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    15/17

    Declare that a variable should only be initialized the first time a method is invoked, and

    its value remembered after that (rarely a good idea for non-const variables):

    void MyClass::sketchy() {static int x = 0;

    }

    Declare that a function or variable should not be visible outside a .cpp file (rarely used):

    static int helper(int y, int z) {Return y + 2 * z;

    }

    18.4 const

    Declare that a method never mutates a value:

    void safeMethod(const std::string& x);

    Declare that a method never mutates any member variable:

    void alsoSafe(int y) const;

    Declare that a member variable is never changed after initialization:

    class Clown {public:

    const Color3 hair;Clown();

    };

    Create a mutable pointer to an immutable int, i.e., dont change the value x is pointing

    at:const int* x;int const* x;

    Create an immutable pointer to a mutable int, i.e., dont reassign the pointer/ dont

    change x:

    int* const x;

  • 8/8/2019 Graphics Programming in C++ With G3D

    16/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    16/17

    18.5 Iterator

    Set keep;Set::Iterator it = keep.begin();Set::Iterator end = keep.end();

    while (it != end) {++it;

    }

    18.6 Function Pointers

    You dont need to use function pointers very often, but if you do, the syntax is odd and

    youll want to check back here.

    Pass a non-static method pointer as an argument:

    debugPane->addButton(Exit, &App::exit);

    Declare a function pointer/static method pointer (e.g., to atan2):

    double(*atan2Ptr)(double, double);

    Create a typedef for a function pointer type:

    typedef double(*)(double, double) ATan2Type;

    Pass a function pointer as an argument or use as a value:

    passPointer( &atan2 );ATan2Type atan2Ptr = &atan2;

    Call a function using its pointer (two syntaxes available):

    atan2Ptr(x, y);(*atan2Ptr)(x, y);

    18.7 printf

    G3D provides three ways of printing text: logPrintf(), debugPrintf(), and screenPrintf().

    You can also print to a std::string with format(). Type man printf to see a full

    specification for these. The common idioms youll use are:

  • 8/8/2019 Graphics Programming in C++ With G3D

    17/17

    CS371 Fall 2008 Williams College

    Prof. McGuire Department of Computer Science

    17/17

    std::string s;int i;float f;debugPrintf(The ints value is %d\n, i);debugPrintf(A string %s and a float %f\n, s.c_str(), f);

    19 Coding Conventions

    Were going to be looking at a lot of each others code between group projects, sample

    code, and helping each other debug. To make it easy to understand code written by others

    and to cut and paste between projects, please follow the following coding conventions. In

    some cases you may find that the conventions are making it harder to read your code.

    When that occurs, write in the way that is easiest for another person to read even if it

    breaks with convention.

    Follow Java coding conventions wherever they apply to C++

    Make header guards case-sensitive, e.g., #ifndef Film_h

    Prefix private and protected member variables with m_, e.g., m_photonMap Indent member variables and method arguments in columns, e.g.,

    int m_numPhotons;

    std::string m_filename;

    Put a space before the open parent of FOR, WHILE, and IF, e.g., if (! b) Put the open-brace on the same line as FOR, WHILE, IF, TRY, etc. Always uses braces, even for single-line FOR, WHILE, etc. Use four-space indenting and no tabs Put comments before the code to which they apply Keep lines to about 80 characters (for printing) Fully-parenthesize logical expressions, e.g., (x || y) && (z > 2) When writing pointers, the * goes with the class name, e.g., RenderDevice* rd;