Title: More on Drawable Objects, Hierarchical Objects
1More on Drawable Objects,Hierarchical Objects
- Glenn G. ChappellCHAPPELLG_at_member.ams.org
- U. of Alaska Fairbanks
- CS 481/681 Lecture Notes
- Wednesday, January 21, 2004
2ReviewDrawable 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.
3ReviewDrawable 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
-
- Thats 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.
4ReviewDrawable 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 Cokebottledraw() const
- Draw a cokebottle here.
5ReviewDrawable 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)
6More on Drawable ObjectsInheritance 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.
- stdvectorltDrawablegt scene
- Why?
- Hint There are two big problems here.
7More on Drawable ObjectsInheritance and
Containers 2/3
- stdvectorltDrawablegt 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.
8More on Drawable ObjectsInheritance and
Containers 3/3
- Solution Use base-class pointers.
- Be sure that objects are deleted properly!
- stdvectorltDrawable gt scene
-
- To add to the scene
- scene.push_back(new Cokebottle)
- To draw the entire scene
- stdvectorltDrawable gtconst_iterator it
- for (it scene.begin() it ! scene.end() it)
- (it)-gtdraw()
9Hierarchical ObjectsOverview
- 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.
10Hierarchical ObjectsIntroduction
- 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.
11Hierarchical ObjectsTransformations 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?).
12Hierarchical ObjectsTransformations 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
13Hierarchical ObjectsTransformations 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).
14Hierarchical ObjectsTransformations 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
15Hierarchical ObjectsExample (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.
16Hierarchical ObjectsExample (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()
17Hierarchical ObjectsExample (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()
18Hierarchical ObjectsExample (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.