The XY plane is the plane of the iPhone screen.
By default, the
torus
is centered at the origin and lies in the XY plane.
The
translate
matrix in the
display
function in
threedimensions.cpp
moves the torus 7 units away from us.
(This puts the torus squarely in the visible zone,
which goes from 1 to 10 units away from us.
See the
SetupRC
function.)
The
rotate
matrix in
display
rotates the torus around the Y axis.
The
modelView
matrix is a combination of
translate
followed by
rotate
.
The projection matrix in the
frustum
make distant objects look smaller and near objects look bigger.
threedimensions.cpp
main.m
GLAppDelegate
GLViewController
EAGLView
GL
.
If you run the project at this point you get a bouncing two-d square.
We will change it to a rotatable three-d torus.
UIApplicationMain
in
main.m
remain
nil
.
Do not remove the
MainWindow.xib
file from the
GL-Info.plist
or from the project.
GL
)
and select
Add → New Group
.
Name the new group
GLTools
.
It isn’t a folder, but it looks like one.
SB5.zip
and
Xcode.zip
from the
OpenGL
SuperBible
home page
into the
Downloads
folder on your Mac.
In
SB5.zip
,
go to the folder
Src/GLTools/include
and copy the 11
.h
files into the root directory of your project.
Then go to the folder
Src/GLTools/src
and copy the 5
.cpp
files into the root folder of your project.
In the Groups & Files pane of Xcode,
select the
GLTools
group you created and add the 16 files to the project:
11
.h
files and 5
.cpp
files.
Project →
Edit Active Target "GL" →
Build →
Search Paths
.
).
Dot means the current directory.
File →
New File…
Choose a template for your new file: Mac OS X C and C++
C++ File
Next
File Name: threedimensions.cpp
(do not create a corresponding .h
file)
Finish
SB5.zip
,
go to the folder
GLTools/GLTools
.
Copy the file
libGLTools.a
into the root directory of your project.
Project →
Edit Active Target "GL" →
General →
Linked Libraries
OpenGLES.framework
to the project.
Press the plus sign again and
Add Other…
to add to the project the
libGLTools.a
you copied into the root folder of your project.
theta
to class
EAGLView
in
EAGLView.h
.
GLfloat theta; //in degreesInitialize it in the
initWithCoder:
method of class
EAGLView
.
theta = 0.0f;Add the property
theta
to class
EAGLView
.
@property (nonatomic, assign) GLfloat theta; //in EAGLView.h
@synthesize theta; //in EAGLView.m
.m
files that call functions written in C++ have to be renamed to
.mm
:
GLViewController.mm
and
EAGLView.mm
.
In the Classes folder of the Groups & Files pane of Xcode,
right-click on the filename and select Rename.
Update the filename in the comment at the top of each file.
EAGLView.mm
immediately after the the
#import
s.
//C++ functions defined in threedimensions.cpp void SetupRC(); void reshape(GLsizei width, GLsizei height); void display(GLfloat theta);
SetupRC
is called in the
createFramebuffer
method of class
EAGLView
immediately before the
glCheckFramebufferStatus
.
reshape
is called in the
setFramebuffer
method of class
EAGLView
in place of the call to
glViewport
.
display
will be called in the following method
touchesMoved:withEvent:
.EAGLView
in
EAGLView.m
.
- (void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event { CGFloat newX = [[touches anyObject] locationInView: self].x; CGFloat oldX = [[touches anyObject] previousLocationInView: self].x; //Swiping across the width of the screen should rotate the torus 180°. //Theta is in degrees. CGFloat dx = newX - oldX; theta += 180.0f * dx / framebufferWidth; [self setFramebuffer]; display(theta); [self presentFramebuffer]; }
GLViewController.mm
immediately after the the
#import
s.
//C++ function defined in threedimensions.cpp void display(GLfloat theta);Call it in the
drawFrame
method of class
GLViewController
,
replacing all of the code between
setFrameBuffer
and
presentFramebuffer
.
display(((EAGLView *)self.view).theta);
EAGLView
already contains a
renderbuffer.
Let’s also give it a depth buffer.
Add the following instance variable to class
EAGLView
in
EAGLView.h
.
GLuint depthRenderbuffer;Insert the following code into the
createFramebuffer
method of class
EAGLView
immediately before the
SetupRC
.
//Create the depth buffer. glGenRenderbuffers(1, &depthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, framebufferWidth, framebufferHeight); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);Insert the following code into the
deleteFramebuffer
method of class
EAGLView
immediately after the two analogous
if
s.
if (depthRenderbuffer) { glDeleteRenderbuffers(1, &depthRenderbuffer); depthRenderbuffer = 0; }
glViewPort
in the function
reshape
in
threedimensions.cpp
makes the picture fill the entire iPhone screen.
What happens if you change
glViewport(0, 0, width, height);to one of the following?
glViewport(0, 0, width / 2, height / 2); glViewport(width / 4, height / 4, width / 2, height / 2);
SetPerspective
member function of the
frustum
in
reshape
allows us to see objects up to 10 units away from us.
This is okay because the
display
function translates the torus only 7 units away.
What hapens if you change the fourth argument of
SetPerspective
from
10.0f
to
7.0f
?
glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST);in
SetupRC
in
threedimensions.cpp
?
modelViewMatrix.Rotate(theta, 0.0f, 1.0f, 0.0f);to the following, and include
<StopWatch.h>
.
static CStopWatch watch; modelViewMatrix.Rotate(watch.GetElapsedSeconds() * 60.f, 0.0f, 1.0f, 0.0f);Then change it back.
threedimensions.cpp
.
GLTriangleBatch torus; GLTriangleBatch sphere;In
SetupRC
,
gltMakeTorus(torus, 1.0f, 1.0f / 3.0f, 40, 40); gltMakeSphere( sphere, .25, //radius 40, 40 );The sphere is centered at
display
,
torus.Draw(); sphere.Draw();
GL_CULL_FACE
and
GL_DEPTH_TEST
.
GLTriangleBatch cylinder;
gltMakeCylinder( cylinder, .25f, //base radius .25f, //top radius .25f, //height 40, 40 );
cylinder.Draw();
0.0f
.
GL_CULL_FACE
.
GLTriangleBatch disk;
gltMakeDisk( disk, .05, //inner radius .25f, //outer radius 40, 40 );
disk.Draw();
GLBatch cube;
gltMakeCube( cube, .25f //half of length of side );
cube.Draw();
100.0f
.
red
to
greenCheese
.
Change the background to midnight blue
(0.09765f
,
0.09765f
,
0.4375f
).
The phases of the moon traditionally start with New Moon,
so change the initial location of the light to the point
0.0f
,
0.0f
,
-100.0f
,
which is behind the moon.
We will rotate the light in a circle that lies in the XZ plane.
In other words, the axis of this circle will be the Y axis.
We negate the elapsed seconds to move from New Moon to First Quarter,
which looks like an uppercase
D
.
Without the negation, we would move from New Moon to Third Quarter
(a backwards uppercase D
):
the phases would run backwards.
UseStockShader
to the following.
//One lunar month per 2pi seconds of real time. static CStopWatch watch; M3DMatrix44f rot; m3dRotationMatrix44(rot, -watch.GetElapsedSeconds(), 0.0f, 1.0f, 0.0f); M3DVector4f current; m3dTransformVector4(current, lightPosition, rot); shaderManager.UseStockShader( GLT_SHADER_POINT_LIGHT_DIFF, modelView, frustum.GetProjectionMatrix(), current, greenCheese );