• Home
  • Textbooks
  • Mathematics for Game Programming and Computer Graphics: Explore the essential mathematics for creating, rendering, and manipulating 3D virtual environments
  • Updating and Drawing the Graphics Environment

Mathematics for Game Programming and Computer Graphics: Explore the essential mathematics for creating, rendering, and manipulating 3D virtual environments

Penny de Byl

Chapter 6

Updating and Drawing the Graphics Environment - all with Video Answers

Educators


Section 1

Technical requirements Introducing the main game loop Updating and drawing objects

Problem 1

Create a new Python script called Helloobject. py. Copy the code from the HelloLights. py you completed in Chapter 5, Let's Light It Up!, or grab a fresh copy of the one included with Chapter 5 on GitHub. You will also need to make copies of Cube . py and Mesh3D.py in the new Chapter 6 folder.

Check back soon!

Problem 2

Create a new Python script called object.py and add the following code:
from Mesh3D import *
class object:
def__init_(self, obj_name) :
self. name $=o b j$ name
self.components $=[]$
def add_component(self, component) :
self.components . append (component)
def update(self) :
for $\mathrm{c}$ in self.components:
if isinstance (c, Mesh3D) :
c.draw ()
In this, we have created a generic object class that is capable of storing multiple components. Components are added with the add_component () method into a list which is then searched through to find components constructed with the Mesh3D class in update () .

Check back soon!

Problem 3

Be sure to include Object at the top of Helloobject.py as in the following:
from Object import *

Check back soon!

Problem 4

Modify the code in Helloobject . py as follows to make use of the new Object class:
.
glTranslatef $(0.0,0.0,-3)$
glEnable (GL_DEPTH_TEST)
cube $=$ Object ("Cube")
cube.add_component (Cube (GL_POLYGON,
ā€œimages/wall.tifā€))
glEnable(GL_LIGHTING)
..

Check back soon!

Problem 5

Run the Helloobject. py script. You will not notice any changes in its current functioning.

Check back soon!

Problem 6

We will now add another component to create a transform. Create a new script called Transform. py and add the following code:
import pygame
class Transform:
def_init_(self, position):
self.set_position(position)
def get_position(self) :
return self.position
def set_position(self, position) :
self.position $=$ pygame. math.Vector3 (position)
This component defines the object's location in 3D space. Notice the use of the Pygame Vector3 class. You will be seeing this a lot. Besides other things, this class stores a tuple in the form of $x, y$, and $z$. This makes the code more intuitive to read.

Check back soon!

Problem 7

To integrate the new Transform class, we need to add it as a component in the Helloobject. py script first as follows:
from Mesh3D import *
from Transform import *
.
class object:
glEnable (GL_DEPTH_TEST)
cube $=$ object ("Cube")
cube.add_component (Transform $((0,0,-1)))$
cube.add_component (Cube (GL_POLYGON,
"images/wall.tif"))
By including the cube.add_component $(\operatorname{Transform}((0,0,-1)))$ line, you are setting the location of the cube to $(0,0,-1)$-however, you must remember that earlier in the code from step 4 we set the following:
glTranslatef $(0.0,0.0,-3)$

This is already offsetting whatever is drawn by $z=-3$. Therefore, by adding the Transform component with $z=-1$, the total offset for the cube will be the addition of these, which equates to $\mathrm{z}=-4$.

Check back soon!

Problem 8

To get the Transform component to affect the drawing, further changes need to be made to the add_component and update functions in the Object class. Therefore, next we test if the component is of type Transform and push it into first position in the list.
from Transform import *
.
def add_component(self, component) :
if isinstance (component, Transform) :
self.components.insert ( 0 , self.components)
self.components.append (component)
def update (self):
for $c$ in self.components:
if isinstance(c, Transform) :
pos $=c \cdot$ get_position ()
glTranslatef(pos.x, pos.y, pos.z)
if isinstance (c, Mesh3D) :
c. draw ()
There are a couple of things to take note of in this code. First, a Transform component is treated as a very special case, and you will see that it is inserted first in the component list. This is because the position needs to be set before any drawing is done by Mesh3D. Next, the update method, before drawing the mesh, calls a glTranslatef () method to ensure that the movement from the Transform component is taken into consideration and sent to OpenGL.

Check back soon!

Problem 9

It's time to test the program out again by running it. Before you do, try and imagine what you might see. I can bet you won't see what's about to come. Run your code (and get it running) before moving on to the next point.
If your cube flew off into the distance, as shown in Figure 6.3, then the program is running as it should:
(Figure can't copy)
Here's what's happening. In each main loop, the object class's update method is called. This runs a glTranslatef () method before drawing the mesh. It does this for every frame. The thing with glTranslatef ( ) is that it's compounding. So, in each loop, $z=-1$ is added to the position of the cube and this animates it flying off into the distance. So, how do we fix this?

Check back soon!

Problem 10

OpenGL has a very clever mechanism. It's able to store the current state of the environment, draw something, and then return to the previous state. What we want to do is basically save the position of the first glTranslate $(0,0,-3)$, add the glTranslate $(0,0,-1)$ method, draw the cube, and then take away the last glTranslate (). We do it by modifying the update method in Object to the following:
def update (self) :
glPushMatrix ()
for $c$ in self.components:
if isinstance(c, Transform) :
pos $=c \cdot$ get_position()
glTranslatef (pos.x, pos.y, pos.z)
if isinstance (c, Mesh3D) :
c. draw ()
glPopMatrix()
In short, think of glPushMatrix () as fixing the graphics environment with respect to transformations (including a translate). This causes OpenGL to remember the current state of the environment. When glPopMatrix () is called, OpenGL returns to the last remembered state of the environment. Don't worry too much about the technical details that are going on in the background at this time, as we will cover them in detail in later chapters.

Check back soon!