1 / 18

Comprehensive Guide: Drawable and Hierarchical Objects

Learn how to represent and manipulate drawable objects independently of their types, discuss OO solutions in C++, handle hierarchical objects conveniently. Understand polymorphism, inheritance, and container usage. Useful for computer science students.

rhollander
Download Presentation

Comprehensive Guide: Drawable and Hierarchical Objects

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. More on Drawable Objects,Hierarchical Objects Glenn G. ChappellCHAPPELLG@member.ams.org U. of Alaska Fairbanks CS 481/681 Lecture Notes Wednesday, January 21, 2004

  2. Review:Drawable Objects [1/4] • Now we begin looking at how to represent a scene internally. • It will be convenient to be able to deal with drawable objects independent of their characteristics. • There are many ways to do this; we discuss the usual OO solution, as implemented in C++. • It is reasonable to represent different kinds of drawable objects with different data types. • Deriving all of these from a common base class (“Drawable”?) allows us to draw them without knowing what type they are. CS 481/681

  3. Review:Drawable Objects [2/4] • Here is one way to write our base class: // class Drawable // Abstract base class for drawable objects class Drawable { public: virtual ~Drawable() {} virtual void draw() const = 0; }; • That’s all! • The destructor is a C++ detail: a base class should have a virtual destructor. • Note that Drawable is an abstract class (due to the “= 0”); we cannot declare objects of type Drawable. CS 481/681

  4. Review:Drawable Objects [3/4] • To declare a drawable object type, do something like this: class Cokebottle : public Drawable { public: virtual ~Cokebottle() {} virtual void draw() const; Cokebottle():Drawable(),iscokeit(false) {} void cokeisit() { iscokeit = true; } private: bool iscokeit; // true if Coke is it. }; void Cokebottle::draw() const { Draw a cokebottle here. } CS 481/681

  5. Review:Drawable Objects [4/4] • To use objects polymorphically, refer to them via base-class pointers or references: void draw_this(const Drawable & obj) { obj.draw(); // Calls the proper virtual function. } • Function draw_this can take a parameter of any type derived from Drawable. • The above code would not work correctly if the object were passed by value. • Now we can do this: Cokebottle c; draw_this(c); CS 481/681

  6. More on Drawable Objects:Inheritance and Containers [1/3] • Since we can deal with all drawable objects the same way, we can stick all of our objects into a container (array, vector, etc.), and iterate through that container to draw the scene. • However, the following will get us into trouble. std::vector<Drawable> scene; • Why? • Hint: There are two big problems here. CS 481/681

  7. More on Drawable Objects:Inheritance and Containers [2/3] std::vector<Drawable> scene; • First, this will not compile. • Since Drawable is an abstract class, we cannot create objects of type Drawable. • But, second, even if we make Drawable a concrete class, this is a problem. • A Cokebottle is probably bigger than a Drawable. So we cannot store a Cokebottle in the space meant for a Drawable. • The problem resulting from trying to store an object of a derived class in a base-class variable is called slicing. CS 481/681

  8. More on Drawable Objects:Inheritance and Containers [3/3] • Solution: Use base-class pointers. • Be sure that objects are delete’d properly! std::vector<Drawable *> scene; • To add to the scene: scene.push_back(new Cokebottle); • To draw the entire scene: std::vector<Drawable *>::const_iterator it; for (it = scene.begin(); it != scene.end(); ++it) (*it)->draw(); CS 481/681

  9. Hierarchical Objects:Overview • Suppose we wish to draw a moving object with moving parts. • This is called a hierarchical object. • See face.cpp for an example. • Two questions: • How can we handle this conveniently with our graphics API? • What sorts of data structures are appropriate for storing such an object? • We begin with the first question. CS 481/681

  10. Hierarchical Objects:Introduction • We think of an object with moving parts as a hierarchy. • At the top of the hierarchy is the object as a whole. • At the next lower level in the hierarchy are the moving parts (for example, the eyes in face.cpp). • Moving parts can contain moving parts; these are at an even lower level in the hierarchy. • We use stack operations to handle the transformations involved in drawing hierarchical objects. CS 481/681

  11. Hierarchical Objects:Transformations [1/4] • We want a moving object to have a moving part. • We should be able to move the object as a whole. • We should also be able to move the part as a part of the object. • Thus, one transformation is applied to the object as a whole, but two transformations are applied to the moving part. • The transformation for the moving part needs to be done before the transformation of the object as a whole (right?); therefore it comes later in the code (right?). CS 481/681

  12. Hierarchical Objects:Transformations [2/4] • Pseudocode: glPushMatrix(); Set up transformation for whole object Draw non-moving parts glPushMatrix(); Multiply current matrix by transformation for moving part Draw moving part glPopMatrix(); glPopMatrix(); • This is the general form of the code to draw a hierarchical object. • What are the push/pop really good for? See the next slide … CS 481/681

  13. Hierarchical Objects:Transformations [3/4] • What if a hierarchical object has more than one moving part? glPushMatrix(); Set up transformation for whole object Draw non-moving parts glPushMatrix(); Multiply current matrix by transformation for moving part 1 Draw moving part 1 glPopMatrix(); glPushMatrix(); Multiply current matrix by transformation for moving part 2 Draw moving part 2 glPopMatrix(); glPopMatrix(); • It is convenient to make some of the pieces above into separate functions. • Then follow this rule: If a function changes a matrix, then it also restores it to its prior value. • This is essentially the form of our example (discussed shortly). CS 481/681

  14. Hierarchical Objects:Transformations [4/4] • What if a hierarchical object has more than two levels? glPushMatrix(); Set up transformation for whole object Draw non-moving parts glPushMatrix(); Multiply current matrix by transformation for moving part 1 Draw moving part 1 glPushMatrix(); Multiply current matrix by transformation for sub-part 1 of moving part 1 Draw sub-part 1 of moving part 1 Etc … CS 481/681

  15. Hierarchical Objects:Example (face.cpp) [1/4] void display() { glClear(GL_COLOR_BUFFER_BIT); // Draw face glPushMatrix(); glTranslated(face_move, 0.0, 0.0); glRotated(face_angle, 0,0,1); glScaled(face_scale, face_scale, face_scale); draw_face(); glPopMatrix(); • This is the beginning of the display routine. It sets up the transformation for the face, then calls draw_face to do the drawing. CS 481/681

  16. Hierarchical Objects:Example (face.cpp) [2/4] • Here is a portion of the code for function draw_face: // Draw head glColor3d(0.8, 0.6, 0.4); glCallList(disk_list); // Draw left eye (on viewer's right) glPushMatrix(); glTranslated( 0.4, 0.3, 0.0); glScaled(0.2, 0.2, 1.0); draw_eye(); glPopMatrix(); // Draw right eye (on viewer's left) glPushMatrix(); glTranslated(-0.4, 0.3, 0.0); glScaled(0.2, 0.2, 1.0); draw_eye(); glPopMatrix(); • Because we handle transformations properly, we can use the same function to draw both eyes. CS 481/681

  17. Hierarchical Objects:Example (face.cpp) [3/4] • Each function can be written to draw its part within a square of side 2, centered at the origin (x & y go from –1 to 1). • Then we set up the transformation to put the part in the proper place before we call the function that draws it. • The function that does the drawing uses the transformation it is given, modifies it if necessary, but always restores it to the original value. void draw_eye() { // Draw white of eye glPushMatrix(); glScaled(1.0, 0.4, 1.0); glColor3d(1.0, 1.0, 1.0); glCallList(disk_list); glPopMatrix(); CS 481/681

  18. Hierarchical Objects:Example (face.cpp) [4/4] • Comments • Each function is written to draw the appropriate part (and all sub-parts) in some kind of “standard position”. • Again, sub-part transformations come before the main transformations, which means they are later in the code, which means they can go in the function that draws the sub-part (right?). • This is all very convenient and easy to use, once you wrap your mind around it. • We are very clear about expectations for matrix mode, etc., when entering and leaving each function. • The initial set-up is done when the reshape function is first called. • After that, whenever a function finishes, we are careful to leave things the way they were when it started. CS 481/681

More Related