iPhone gluProject and gluUnProject

Now that I’ve started to do some OpenGL I needed to determine where in my 3D world a user had touched. In order to do this you can use a function called gluUnProject, which isn’t part of the standard framework. I’ve converted the code (so have many others) and posted it here in case it might help someone else.

I used the following article to then use this methods.

/* implementation de gluProject et gluUnproject */
/* M. Buffat 17/2/95 */

/*
 * Transform a point (column vector) by a 4x4 matrix.  I.e.  out = m * in
 * Input:  m - the 4x4 matrix
 *         in - the 4x1 vector
 * Output:  out - the resulting 4x1 vector.
 */
static void
transform_point(GLfloat out[4], const GLfloat m[16], const GLfloat in[4])
{
#define M(row,col)  m[col*4+row]
	out[0] = M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
	out[1] = M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
	out[2] = M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
	out[3] = M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
#undef M
}

/*
 * Perform a 4x4 matrix multiplication  (product = a x b).
 * Input:  a, b - matrices to multiply
 * Output:  product - product of a and b
 */
static void
matmul(GLfloat * product, const GLfloat * a, const GLfloat * b)
{
	/* This matmul was contributed by Thomas Malik */
	GLfloat temp[16];
	GLint i;

#define A(row,col)  a[(col<<2)+row]
#define B(row,col)  b[(col<<2)+row]
#define T(row,col)  temp[(col<<2)+row]

	/* i-te Zeile */
	for (i = 0; i < 4; i++) {
		T(i, 0) = A(i, 0) * B(0, 0) + A(i, 1) * B(1, 0) + A(i, 2) * B(2, 0) + A(i, 3) * B(3, 0);
		T(i, 1) = A(i, 0) * B(0, 1) + A(i, 1) * B(1, 1) + A(i, 2) * B(2, 1) + A(i, 3) * B(3, 1);
		T(i, 2) = A(i, 0) * B(0, 2) + A(i, 1) * B(1, 2) + A(i, 2) * B(2, 2) + A(i, 3) * B(3, 2);
		T(i, 3) = A(i, 0) * B(0, 3) + A(i, 1) * B(1, 3) + A(i, 2) * B(2, 3) + A(i, 3) * B(3, 3);
	}

#undef A
#undef B
#undef T
	memcpy(product, temp, 16 * sizeof(GLfloat));
}

/*
 * Compute inverse of 4x4 transformation matrix.
 * Code contributed by Jacques Leroy jle@star.be
 * Return GL_TRUE for success, GL_FALSE for failure (singular matrix)
 */
static GLboolean
invert_matrix(const GLfloat * m, GLfloat * out)
{
	/* NB. OpenGL Matrices are COLUMN major. */
#define SWAP_ROWS(a, b) { GLfloat *_tmp = a; (a)=(b); (b)=_tmp; }
#define MAT(m,r,c) (m)[(c)*4+(r)]

	GLfloat wtmp[4][8];
	GLfloat m0, m1, m2, m3, s;
	GLfloat *r0, *r1, *r2, *r3;

	r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3];

	r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1),
	r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3),
	r0[4] = 1.0f, r0[5] = r0[6] = r0[7] = 0.0f,
	r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1),
	r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3),
	r1[5] = 1.0f, r1[4] = r1[6] = r1[7] = 0.0f,
	r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1),
	r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3),
	r2[6] = 1.0f, r2[4] = r2[5] = r2[7] = 0.0f,
	r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1),
	r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3),
	r3[7] = 1.0f, r3[4] = r3[5] = r3[6] = 0.0f;

	/* choose pivot - or die */
	if (fabsf(r3[0]) > fabsf(r2[0]))
		SWAP_ROWS(r3, r2);
	if (fabsf(r2[0]) > fabsf(r1[0]))
		SWAP_ROWS(r2, r1);
	if (fabsf(r1[0]) > fabsf(r0[0]))
		SWAP_ROWS(r1, r0);
	if (0.0f == r0[0])
		return GL_FALSE;

	/* eliminate first variable     */
	m1 = r1[0] / r0[0];
	m2 = r2[0] / r0[0];
	m3 = r3[0] / r0[0];
	s = r0[1];
	r1[1] -= m1 * s;
	r2[1] -= m2 * s;
	r3[1] -= m3 * s;
	s = r0[2];
	r1[2] -= m1 * s;
	r2[2] -= m2 * s;
	r3[2] -= m3 * s;
	s = r0[3];
	r1[3] -= m1 * s;
	r2[3] -= m2 * s;
	r3[3] -= m3 * s;
	s = r0[4];
	if (s != 0.0f) {
		r1[4] -= m1 * s;
		r2[4] -= m2 * s;
		r3[4] -= m3 * s;
	}
	s = r0[5];
	if (s != 0.0f) {
		r1[5] -= m1 * s;
		r2[5] -= m2 * s;
		r3[5] -= m3 * s;
	}
	s = r0[6];
	if (s != 0.0f) {
		r1[6] -= m1 * s;
		r2[6] -= m2 * s;
		r3[6] -= m3 * s;
	}
	s = r0[7];
	if (s != 0.0f) {
		r1[7] -= m1 * s;
		r2[7] -= m2 * s;
		r3[7] -= m3 * s;
	}

	/* choose pivot - or die */
	if (fabsf(r3[1]) > fabsf(r2[1]))
		SWAP_ROWS(r3, r2);
	if (fabsf(r2[1]) > fabsf(r1[1]))
		SWAP_ROWS(r2, r1);
	if (0.0f == r1[1])
		return GL_FALSE;

	/* eliminate second variable */
	m2 = r2[1] / r1[1];
	m3 = r3[1] / r1[1];
	r2[2] -= m2 * r1[2];
	r3[2] -= m3 * r1[2];
	r2[3] -= m2 * r1[3];
	r3[3] -= m3 * r1[3];
	s = r1[4];
	if (0.0f != s) {
		r2[4] -= m2 * s;
		r3[4] -= m3 * s;
	}
	s = r1[5];
	if (0.0f != s) {
		r2[5] -= m2 * s;
		r3[5] -= m3 * s;
	}
	s = r1[6];
	if (0.0f != s) {
		r2[6] -= m2 * s;
		r3[6] -= m3 * s;
	}
	s = r1[7];
	if (0.0f != s) {
		r2[7] -= m2 * s;
		r3[7] -= m3 * s;
	}

	/* choose pivot - or die */
	if (fabsf(r3[2]) > fabsf(r2[2]))
		SWAP_ROWS(r3, r2);
	if (0.0f == r2[2])
		return GL_FALSE;

	/* eliminate third variable */
	m3 = r3[2] / r2[2];
	r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4],
	r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7];

	/* last check */
	if (0.0f == r3[3])
		return GL_FALSE;

	s = 1.0f / r3[3];		/* now back substitute row 3 */
	r3[4] *= s;
	r3[5] *= s;
	r3[6] *= s;
	r3[7] *= s;

	m2 = r2[3];			/* now back substitute row 2 */
	s = 1.0f / r2[2];
	r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),
	r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);
	m1 = r1[3];
	r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,
	r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;
	m0 = r0[3];
	r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,
	r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;

	m1 = r1[2];			/* now back substitute row 1 */
	s = 1.0f / r1[1];
	r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),
	r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);
	m0 = r0[2];
	r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,
	r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;

	m0 = r0[1];			/* now back substitute row 0 */
	s = 1.0f / r0[0];
	r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),
	r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);

	MAT(out, 0, 0) = r0[4];
	MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6];
	MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4];
	MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6];
	MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4];
	MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6];
	MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4];
	MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6];
	MAT(out, 3, 3) = r3[7];

	return GL_TRUE;

#undef MAT
#undef SWAP_ROWS
}

/* projection du point (objx,objy,obz) sur l'ecran (winx,winy,winz) */
//GLint GLAPIENTRY;
static GLboolean gluProject(GLfloat objx, GLfloat objy, GLfloat objz,
					 const GLfloat model[16], const GLfloat proj[16],
					 const GLint viewport[4],
					 GLfloat * winx, GLfloat * winy, GLfloat * winz)
{
	/* matrice de transformation */
	GLfloat in[4], out[4];

	/* initilise la matrice et le vecteur a transformer */
	in[0] = objx;
	in[1] = objy;
	in[2] = objz;
	in[3] = 1.0f;
	transform_point(out, model, in);
	transform_point(in, proj, out);

	/* d'ou le resultat normalise entre -1 et 1 */
	if (in[3] == 0.0f)
		return GL_FALSE;

	in[0] /= in[3];
	in[1] /= in[3];
	in[2] /= in[3];

	/* en coordonnees ecran */
	*winx = viewport[0] + (1.0f + in[0]) * viewport[2] / 2.0f;
	*winy = viewport[1] + (1.0f + in[1]) * viewport[3] / 2.0f;
	/* entre 0 et 1 suivant z */
	*winz = (1.0f + in[2]) / 2.0f;
	return GL_TRUE;
}

/* transformation du point ecran (winx,winy,winz) en point objet */
//GLint GLAPIENTRY
static GLboolean gluUnProject(GLfloat winx, GLfloat winy, GLfloat winz,
					   const GLfloat model[16], const GLfloat proj[16],
					   const GLint viewport[4],
					   GLfloat * objx, GLfloat * objy, GLfloat * objz)
{
	/* matrice de transformation */
	GLfloat m[16], A[16];
	GLfloat in[4], out[4];

	/* transformation coordonnees normalisees entre -1 et 1 */
	in[0] = (winx - viewport[0]) * 2.0f / viewport[2] - 1.0f;
	in[1] = (winy - viewport[1]) * 2.0f / viewport[3] - 1.0f;
	in[2] = 2.0f * winz - 1.0f;
	in[3] = 1.0f;

	/* calcul transformation inverse */
	matmul(A, proj, model);
	invert_matrix(A, m);

	/* d'ou les coordonnees objets */
	transform_point(out, m, in);
	if (out[3] == 0.0f)
		return GL_FALSE;
	*objx = out[0] / out[3];
	*objy = out[1] / out[3];
	*objz = out[2] / out[3];
	return GL_TRUE;
}


Found this great article that shows how to scroll the view so that a textfield is in view when the keyboard appears.

http://de-co-de.blogspot.com/2009/03/moving-uitextfield-above-keyboard.html

I’ve copied the code below so that I have a reference to it as well.

You also need to ensure the following steps are followed…

  • Make your controller implement UITextViewDelegate
  • setup your textfield’s (s) delegate to be your controller
  • in the controller @interface add an int verticalOffset
  • in the controller @implementation add the functions below
  • #pragma mark "-- text editing support --"
    // Animate the entire view up or down, to prevent the keyboard from covering the text field.
    - (void)moveView:(int)offset {
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.3];
        // Make changes to the view's frame inside the animation block. They will be animated instead
        // of taking place immediately.
        CGRect rect = self.view.frame;
        rect.origin.y -= offset;
        rect.size.height += offset;
        self.view.frame = rect;
    
        [UIView commitAnimations];
    }
    
    - (BOOL)textFieldShouldReturn:(UITextField *) sender {
        [sender resignFirstResponder];
        if (verticalOffset!=0) {
            [self moveView: -verticalOffset];
            verticalOffset = 0;
        }
        return TRUE;
    }
    
    - (void)textFieldDidBeginEditing:(UITextField *)theTextField {
        int wantedOffset = theTextField.frame.origin.y-200;
        if ( wantedOffset < 0 ) {
            wantedOffset = 0;
        }
        if ( wantedOffset != verticalOffset ) {
            [self moveView: wantedOffset - verticalOffset];
            verticalOffset = wantedOffset;
        }
    }
    


Core Data – fetch a single entity

I’ve used the following code to retrieve an entity from Core Data, so that the result returns the entity object rather than a Dictionary

-(Target *) currentTargetInContext:(NSManagedObjectContext *)context {
	Target *match;

	NSFetchRequest *request = [[NSFetchRequest alloc] init];
	NSEntityDescription *entity = [NSEntityDescription entityForName: @"Target" inManagedObjectContext:context];
	[request setEntity:entity];

	[request setResultType:NSManagedObjectResultType];

	NSPredicate *predicate = [NSPredicate predicateWithFormat:@"Achieved == 0 "];
	[request setPredicate:predicate];

	NSError *error;
	NSArray *objects = [context executeFetchRequest:request error:&error];
	match = nil;
	if (objects == nil)
	{
		// handle the error
	}
	else
	{
		if ([objects count] > 0)
		{
			match = (Target *)[objects objectAtIndex:0];
		}
	}

	[request release];

	return match;
}


UIActionSheet in a tab view

I was getting problems displaying an action sheet in a view that was contained within a tab control.

UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you wish to continue?"
									   delegate:self
									  cancelButtonTitle:@"No"
								   destructiveButtonTitle:@"Yes"
									  otherButtonTitles:nil];
[sheet showInView:self.view];
[sheet release];

The prompt would be displayed but the clicking of the “No” button didn’t always work. The problem is although there is a view that can display the prompt, the correct view to do it is the tab bar, The following code works fine

UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you wish to continue?"
									   delegate:self
									  cancelButtonTitle:@"No"
								   destructiveButtonTitle:@"Yes"
									  otherButtonTitles:nil];
[sheet showFromTabBar:self.tabBarController.tabBar];
[sheet release];


CoreData – SQLite Manager for Firefox

This Firefox add-on allows you to view the data you are running against while using the iPhone simulator. Your sqlite files can be found in the following folder

/Users/”user name”/Library/Application Support/iPhone Simulator/User/Applications/”application guid”/Documents

SQLite Firefox add-on

This is a great tool for view the data you’ve created within your apps, but I would suggest that is best to define your data model via Xcode


In order to move the cursor automatically to the next data entry field when the user presses Next on the keyboard you need to resignFirstResponder from the current field and assign it to the next field using becomeFirstResponder

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
	if (textField == field1TextField) {
		[textField resignFirstResponder];
		[field2TextField becomeFirstResponder];
	}
	else if (textField == field2TextField) {
		[textField resignFirstResponder];
		[field3TextField becomeFirstResponder];
	}
	else if (textField == field3TextField) {
		[textField resignFirstResponder];
	}
	return YES;
}


You need to set the Return Key types accordingly within the interface builder. Your ViewController must also implement UITextFieldDelegate and the textfields delegate assignment must be set via IB to the File’s Owner

See what I do


Provisioning 3.0.1 iPhones

If you’ve updated your development phones to have the latest OS, 3.0.1, then you won’t be able to load you apps onto the device without first following this apple advisory

2. Copy and paste the following line into Terminal:

ln -s /Developer/Platforms/iPhoneOS.platform/DeviceSupport/3.0\ \(7A341\) / Developer/Platforms/iPhoneOS.platform/DeviceSupport/3.0.1

But theres a catch, if you copy the text they have from the document into Terminal there is an extra space created before the second “Developer” path, remove that and all should be ok.

i.e. the command line should be…

ln -s /Developer/Platforms/iPhoneOS.platform/DeviceSupport/3.0\ \(7A341\) /Developer/Platforms/iPhoneOS.platform/DeviceSupport/3.0.1


Data Model change causes iPhone Simulator to fail

When you alter your underlying data model the iPhone simulator will not be able to load your app as the cached version of the database doesn’t match the new one. The easiest way to resolve the problem is to run the simulator, click and hold your app and remove it like you would remove an app from your actual phone.


iPhone Development starter book

If you want to get into iPhone development this is a great book to start from Beginning iPhone Development by apress


Next Entries