Errata

iPhone 3D Programming

Errata for iPhone 3D Programming

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released.

The following errata were submitted by our customers and have not yet been approved or disproved by the author or editor. They solely represent the opinion of the customer.

Color Key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted by Date submitted
A.3
middle

The method in Matrix.hpp for multiplying two Matrices with signature:

Matrix4 operator * (const Matrix4& b) const

is implemented incorrectly. The result it gives for A * B is actually B * A (which is different because matrices don't commute).

Alex Hunsley  Sep 03, 2011 
A.4
middle

In the Quaternion.hpp file, the CreateFromVectors method contains the following lines at the top:

if (v0 == -v1)
return QuaternionT<T>::CreateFromAxisAngle(vec3(1, 0, 0), Pi);

This strategy for when the two input vectors oppose each other is flawed and won't work usually. (It only works when v0 and v1 are at right angles to the X axis.)

Alex Hunsley  Sep 03, 2011 
Printed Page A.4
middle

In Quaternion.hpp, in the method with signature:

Quaternion::Slerp(T t, const QuaternionT<T>& v1) const

there is an error in the 'use linear interoplation for small angles' edge case. It results in Slerp doing animation backwards for small angles.

The line which reads:

QuaternionT<T> result = v1 + (*this - v1).Scaled(t);

should actually be:

QuaternionT<T> result = *this + (v1 - *this).Scaled(t);

Alex Hunsley  Sep 14, 2011 
Other Digital Version 1
in sample code

with iOS 5 and Xcode 4.2, running the antialiasing demos results in a crash. It appears that

GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);

returns an error and "Incomplete FBO" is output to the console.

bob sabiston  Oct 22, 2011 
Other Digital Version 1
sample code

The "Trefoil.Stencil" project crashes with this message:


2011-10-22 12:59:58.245 Stencil[5995:707] Applications are expected to have a root view controller at the end of application launch


This is on ioS5 SDK, Xcode 4.2

Thanks
Bob

Bob Sabiston  Oct 22, 2011 
Printed Page 12
Bottom

Confirmed previous submission:

Its serious as the example will not run.

Linking in the OpenGL and Quartz Libraries:

The paragraph tells you to load OpenGLES.framework and QuartzCore.framework but since we started with OpenGL template they were already loaded. However,

I needed to explicitly load CoreGraphics.framework for CGRectGetWidth() and CGRectGetWidth() to work.
?

Anonymous  Jan 11, 2011 
Printed Page 13
Top of the page

The paragraph tells you to load OpenGLES.framework and QuartzCore.framework but since we started with OpenGL template they were already loaded. However, I needed to explicitly load CoreGraphics.framework for CGRectGetWidth() and CGRectGetWidth() to work.

Anonymous  Aug 15, 2010 
PDF Page 17
Underneath the note about memory management in Objective-C

What is the diference between glGenFramebuffers and glGenFramebuffersOES? The XCode generated template uses the former.

Pedro Remedios  Jul 02, 2011 
Printed Page 23
Example 1.6

In that example GLView.h imports IRenderingEngine.hpp, but at its time GLView.h is imported by the app Delegate so inserting c++ code in a pure objective-c class and bringing in an error at the 'virtual' keyword of "struct IRenderingEngine* createRenderer1();"
I also submitted the case to the Apple Developer Forum where I was suggested to substitute the import with a forward struct declaration. Ref: EAGL rendering API a.

Fabrizio Bartolomucci  Oct 12, 2010 
Printed Page 23
Example 1.6

In Example 1-6 on page 23, m_timestamp is declared as a float, but it should be a double.

This may seem like a small thing, but it ends up being the root cause behind why the arrow rotation is so shaky.

m_timestamp is set from displayLink's timestamp property which is declared as type CFTimeInterval, which in turn is a typedef for double.

When I displayed the values for displayLink.timestamp and m_timestamp, the former kept changing while the latter appeared constant for about six iterations. This in turn caused the computed elapsedSeconds sent to UpdateAnimation() to fluctuation substantially.

I had tried cacheing the value of displayLink.timestamp in drawView: (page 25) so that the value used to compute elapsedSeconds would be the exact same value saved for later in m_timestamp, but then the cached value appeared to remain constant for a few iterations as well. That's when I discovered the type issue.

Glenn  Dec 05, 2010 
Printed Page 24
Example 1-7

The dealloc method for GLView should free the rendering engine. e.g. "delete m_renderingEngine;"

Anonymous  Aug 25, 2010 
Printed Page 26
example 1.8

There are only three float initializers for the RGBA values in the second triangle in the array of vertices. As a result their alpha values are not set to 1.

Simon Winder  Sep 08, 2011 
Printed Page 30
absolute 2nd line from the top of the page

The line reads:

void UpdateAnimation(float timeStep);

when it should read

void UpdateAnimation(float timeStep){}

The difference being at the very end of the line. It reads a semi-colon instead of the correct closed set of parenthesis. I believe this is because the this method has yet to be implemented at this stage in the writing of the code. It is still a stub.

Eric Brotto  Oct 31, 2010 
Printed Page 30
absolute 2nd line from the top of the page

The line reads:

void UpdateAnimation(float timeStep);

when it should read

void UpdateAnimation(float timeStep){}

The difference being at the very end of the line. It reads a semi-colon instead of the correct closed set of parenthesis. I believe this is because the this method has yet to be implemented at this stage in the writing of the code. It is still a stub.

Eric Brotto  Oct 31, 2010 
Printed Page 38
absolute 7th line from the top of the page

The include files read as this:

"../Shaders/Simple.vert"

when they should be printed as this:

"Shaders/Simple.vert"

In other words you need to get rid of the ../ at the beginning.

Eric Brotto  Oct 31, 2010 
Printed Page 64
First code example

The LinearTween function as written has the opposite behavior as the charts in Figure 2.15.

It currently is printed as follows:

--- return t * start + (1 - t) * end;

This causes the value to tween from end to start as t goes from 0 to 1. Instead, it should be listed as this:

--- return (1 - t) * start + t * end;

This correction will allow the tween to go from start to end as t goes from 0 to 1 which is the expected behavior as reinforced by the graphic in Figure 2.15.

Please note that the QuadraticEaseIn function and the QuadraticEaseInOut function (Example 2-4) also rely on this function, so all three do not work properly as a result of this error.

Adam Behringer  Jun 18, 2010 
Printed Page 64
LinearTween function

I think

float LinearTween(float t, float start, float end)
{
return t*start + (1-t)*end;
}

should be


float LinearTween(float t, float start, float end)
{
return (1-t)*start + t*end;
}

since t goes from 0 to 1.

I dont know, this is just my personal thought.

Anonymous  Jun 27, 2010 
Other Digital Version 64
First code example

Just the same mistake in linear tweening equation (should be read as "return (1-t)*start + t*end;", as Adam Behringer already mentioned), but i have it in ePUB edition.

Anton M  Oct 09, 2010 
Printed Page 64
LinearTween function after paragraph 3.

Your calculation for LinearTween should be

float LinearTween(float t, float start, float end) {
return t*end + (1 - t) * start ;
}

t should go from 0 to 1 as elapsed time increases where t = (elapsed time) / (desired duration).

Steve McFarlin  Oct 12, 2010 
PDF Page 65
Example 2-4

The second half is just repeating the first half.

I think the following code should change from:
float QuadraticEaseInOut(float t, float start, float end)
{
float middle = (start + end) / 2;
t = 2 * t;
if (t <= 1)
return LinearTween(t * t, start, middle);
t -= 1;
return LinearTween(t * t, middle, end);
}

to:
float QuadraticEaseInOut(float t, float start, float end)
{
float middle = (start + end) / 2;
t = 2 * t;
if (t <= 1)
return LinearTween(t * t, start, middle);
t -= 1;
/*******************************/
return LinearTween((1-t) * (1-t), middle, end);
/*******************************/
}

Kenny  Jun 07, 2010 
PDF Page 65
Quadratic Ease In Out

The second part of the ease-in-out function repeats the ease in (but translated). I haven't very carefully checked this, but I believe the ease out should be a flipped parabola, translated such that it connects with the the (0.5, middle) point in (t, f(t)) space and scaled appropriately.

So I get this:
Parabola 1: f(t) = t^2
Parabola 2: f(t) = -t^2

>>> move upside down parabola to (2,2*end) -- this is moving it to 2 x (1, end); so (x,y) |--> (x-2,y-2*end)

f(t) = -(t-2)^2 + 2*end

>>> normalize both parabolas so that they start at 0 and end at 1 (as opposed to ending at 2), they must get there twice as fast to fit both the parabolas in space where one parabola used to be; so (t) |--> (2t)

>>> normalize the "heights" of the parabolas so that the composite function and the second parabola both end at (1, end) instead of (1, 2*end); so f(t) |--> f(t) / 2

which leaves:

Parabola 1: f(t) = ( (2t)^2 ) /2
Parabola 2: f(t) = ( -(2t-2)^2 + 2*end ) /2

f(t) = { ( (2t)^2 ) /2 } where t <= 0.5 and { ( -(2t-2)^2 + 2*end ) /2 } where t > 0.5

So I think the last line of code should be (but I haven't checked whether I mapped the above composite function to the "code" properly):

return LinearTween( ( (-1) * (2t-2) * (2t-2) + 2*end ) /2, middle, end) ;

aeb  Nov 06, 2010 
Printed Page 89-94

After making the changes to RenderingEngine1 and RenderingEngine2 the rotate functionality became impaired.

i.e. OnFingerDown did not rotate to the new touchpoint
and moving did not match where you were touching.

Anonymous  Jul 08, 2012 
Printed Page 96
Last paragraph

The sentence in parenthesis should change from

(The legal combination of size and type were covered in the previous chapter in Table 2-1.)

To

(The legal combination of size and type were covered in the previous chapter in Table 2-2.)

Steve McFarlin  Oct 12, 2010 
Printed Page 99
second paragraph

... WireframeSkeleton project from this book's example code (available at http://oreilly.com/catalog/9780596804831)

should read http://examples.oreilly.com/9780596804831/
or
http://my.safaribooksonline.com/9781449388133?bookview=overview

However the WireframeSkeleton is not part of the example code!

rowland shepard  Aug 08, 2010 
Printed Page 107
Second function

Visual visual;
The address of this struct
is being passed to IRenderingEngine::Render as the address of a vector.

rowland shepard  Aug 12, 2010 
PDF Page 107
ApplicationEngine::Render() function

The text states:

void ApplicationEngine::Render() const
{
Visual visual;
visual.Color = m_spinning ? vec3(1, 1, 1) : vec3(0, 1, 1);
visual.LowerLeft = ivec2(0, 48);
visual.ViewportSize = ivec2(320, 432);
visual.Orientation = m_orientation;
m_renderingEngine->Render(vis);
}

On page 105 however, the interface for "->Render" looks like this:
virtual void Render(const vector<Visual>& visuals) const = 0;
In other words, expecting a vector of Visuals - instead of a single Visual.

A possible fix would be:

void ApplicationEngine::Render() const
{
Visual visual;
visual.Color = m_spinning ? vec3(1, 1, 1) : vec3(0, 1, 1);
visual.LowerLeft = ivec2(0, 48);
visual.ViewportSize = ivec2(320, 432);
visual.Orientation = m_orientation;

vector<Visual> vis; // empty vector
vis.push_back (visual); // push the above visual into it
m_renderingEngine->Render(vis);
}

rboerdijk  Aug 15, 2010 
Printed Page 135
Last bullet point

The last bullet point should be labeled '6' and not '2'.

Steve McFarlin  Oct 20, 2010 
Printed Page 139
Example 4-10

The directions state: "Example 4-10 shows the new Initialize method (unchanged portions are replaced with ellipses for brevity)."

Then code example is written as such:

void RenderingEngine::Initialize(const vector<ISurface*> &surfaces) {
vector<ISurface*>::const_iterator surface;
for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {

// Create VBO for the vertices
vector<float> vertices;
(*surface)->GenerateVertices(vertices, VertexFlagsNormals);
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(vertices[0]), &vertices[0], GL_STATIC_DRAW);

// Create a new VBO for the indices if needed.
...
}

// Set up various GL state.
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);

// Set up the material properties.
vec4 specular(0.5f, 0.5f, 0.5f, 1);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular.Pointer());
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0f);

m_translation = mat4::Translate(0,0,-7);
}


Though this is a huge problem, because within this area, there should have been an ellipse as well:

// Create a new VBO for the indices if needed.
...
}

// ellipses should have been placed here

// Set up various GL state.

Following those instructions explicitly leads you to omit:

// Extract width and height from the color buffer.
int width, height;
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &width);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &height);

// Create a depth buffer that has the same size as the color buffer.
glGenRenderbuffersOES(1, &m_depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, width, height);

// Create the framebuffer object.
GLuint frameBuffer;
glGenFramebuffersOES(1, &frameBuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, frameBuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, m_colorRenderbuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, m_depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);


Which in absence makes the program draw a blank magenta screen.

This is a serious technical problem, since, though I found a quick fix to the issue, I don't know if this is the appropriate way to handle this now. I'm under the impression that we should no longer use the colorRenderbuffer since our implementation of the light sourcee; this code is clearly using the colorRenderbuffer.

What should be done here?

Matisse VerDuyn  Mar 06, 2012 
Printed Page 150
Example 4-17

SimpleLighting.Vert
has three attributes
and seven uniforms

but RenderingEngine::Initialize
// extract the handles to attributes and uniforms

extracts six attributes
and four uniforms

the names are also 'out of sync' with the shader.

Review of the sample code ObjViewer
reveals the printed Initialize method to be incorrect.


rowland shepard  Aug 18, 2010 
PDF Page 150
structure definition on page 150 - with consequences in code on page 151 and 152

Just noticed rowland shepard also reported this - here a few details on how to change to code so it works.

See Example 4-15 (SimpleLighting.vert) - it defines AmbientMaterial, SpecularMaterial and Shininess as uniform. On page 150, these three are incorrectly added to the attributehandles (instead of the uniformhandles struct). It should look like this:

struct UniformHandles
{
GLuint Modelview;
GLuint Projection;
GLuint NormalMatrix;
GLuint LightPosition;

GLint Ambient;
GLint Specular;
GLint Shininess;
};

struct AttributeHandles
{
GLint Position;
GLint Normal;
GLint Diffuse;
};

At the bottom of page 151 (continued of page 152) the relevant code should be changed to:

// Extract the handles to attributes and uniforms.
<...>
m_uniforms.Ambient=glGetUniformLocation(program, "AmbientMaterial");
m_uniforms.Specular=glGetUniformLocation(program,"SpecularMaterial");
m_uniforms.Shininess=glGetUniformLocation(program, "Shininess");

<...>

// and don't forget that it's not an attribute but a uniform now -
// so we have to change the gl-call also

// Set up some default material parameters.
glUniform3f(m_uniforms.Ambient, 0.04f, 0.04f, 0.04f);
glUniform3f(m_uniforms.Specular, 0.5, 0.5, 0.5);
glUniform1f(m_uniforms.Shininess, 50);


... after these changes, you'll see the specular white spot :)

rboerdijk  Aug 18, 2010 
Printed Page 236
Trefoil.Stencil example code

I've been following the examples in the book successfully but cannot get the Trefoil.Stencil code to work properly (Trefoil.FakeStencil is fine). I see the loading graphic but then it goes to a pink screen on the simulator and a black screen on my iPhone 4.

I have tried both the version from the monolithic zip file and downloaded the individual sample.

I'm using XCode 4.

Do I need to make changes to get it to work?

Anonymous  Mar 26, 2011 
Printed, PDF Page 242
Example 6-10

According to the Apple OpenGL Programming Guide (http://bit.ly/9IIK5p):

"In iOS 4.0 and later, separate stencil buffers are not supported. Use a combined depth/stencil buffer."

As a result, example 6-10 produces GL_FRAMEBUFFER_UNSUPPORTED_OES when glCheckFramebufferStatusOES() is called.

The solution is to:

- eliminate m_renderbuffers.BigStencil and the associated renderbuffer calls.

- call glRenderbufferStorageOES( GL_RENDERBUFFER_OES, GL_DEPTH24_STENCIL8_OES, size.x, size.y ) to create a packed depth/stencil buffer as in example 6-2

Benj Carson  Jul 25, 2011 
PDF Page 259
2nd line

As a description of the float theta, it says "Azimuth in degrees. This is the horizontal angle off east." But is it "off north?"

Kazuya Abbey  Feb 21, 2011 
Printed Page 286
last line

The current version of PVRTexTool uses different options than the version described in the book -- and I am unable to obtain an earlier version of the tool.

In particular, the command
os.system ("PVRTexTool -h -yflip1 -fOGL8 -iNumeralsTexture.png")

needs to be updated to work with the current tool. I think some of the changes are obvious:

os.system ("PVRTexToolCLI - flip y -i NumeralsTexture.png -o NumeralsTexture.h")

but I have been unable to figure out how to correctly replace the "-fOGL8" option. (I've tried a number of variations -- and get output, but none of my variations reproduce the provided NumeralsTexture.h file.)

Also -- thanks so much for a fantastic book!

Anonymous  Sep 05, 2014 
PDF Page 290
FPSRenderer class

There are two implementations of this class in the example project:

FpsRenderer.DrawTex.h
and
FpsRenderer.Vbo.h

The DrawTex version works correctly, and is the "easier" of the two implementations. However, it is not portable to desktop OpenGL, and it is claimed on pg 293, is not supported in OpenGL ES 2.0.

The VBO implementation does not seem to work. By changing the include at the top of the RenderingEngine class from
#include "FpsRenderer.DrawTex.h"
to
#include "FpsRenderer.Vbo.h",

the frame rate text disappears, without generating an error message to indicate what is wrong.

If there is a bug in the sample code and text, please advise.

Phaedon Sinis  Dec 01, 2010 
Other Digital Version 388
second paragraph, first sentance

Says Example 2-6 shows 'RenderingEngine1.cpp', should be 'RenderingEngine1.h' since it is the declaration not the definition.

Also the definition of Vertex is moved from the .cpp file, where it was in prior examples, to the .h file which (I believe, but I could be wrong) would conflict with the Vertex declaration in RenderingEngine2.h. Granted the paragraph above says the definition of Vertex is "moved higher up in the file" but that's hardly the case since it is in fact moved to the header file.

Brooks Adcock  Oct 12, 2011 
Printed Page 390
methods: operator/ operator* operator/= operator *=

In the Vector2 struct, these methods accept parameters of type float, while in the Vector3 struct, they accept parameters of type T. Is there a reason for this difference?

Tony Wetmore  Aug 22, 2010 
Printed Page 395
Matrix4<T> Class

The function

Vector4<T> operator * (const Vector4<T>& b) const

Is multiplying incorrectly. It is setup to multiply column vectors, and not row vectors. i.e. It is multiplying Av and not vA.

This does not work as expected

mat4 m = mat4::Translate(2,3,4)
vec4 u = vec4(2,2,2,1)
vec4 v = m * u

v = 2,2,2,19

This function should be

Vector4<T> operator * (const Vector4<T>& b) const
{

Vector4<T> v;
v.x = x.x * b.x + y.x * b.y + z.x * b.z + w.x * b.w;
v.y = x.y * b.x + y.y * b.y + z.y * b.z + w.y * b.w;
v.z = x.z * b.x + y.z * b.y + z.z * b.z + w.z * b.w;
v.w = x.w * b.x + y.w * b.y + z.w * b.z + w.w * b.w;

return v;
}

Steve McFarlin  Oct 13, 2010 
Printed Page 395
Matrix Library

The matrix library assumes row-major with row-ordering (i.e. transposed ordering rather than canonical). That's fine except that the library only provides an overload for matrix*vector, but not vector*matrix. However, in a row-major system, you can only multiply a vector to the left of a matrix (as the book itself explains early on). To require a vector on the right side of a matrix in a row-major library is a serious source of confusion and bugs. It's not clear if the calculation is actually doing what is expected.

Jerry Fenly  Jun 06, 2013