mpatric.com

The Texture2D class was included with Apple's CrashLanding iOS sample application. Many developers pulled it out of this sample app to use in their own apps as a quick and easy way of rendering 2D images of any size and a variety of colour formats in OpenGL. I find it useful, particularly when prototyping ideas, however the class is limited in its functionality. I developed a small set of extensions for Texture2D for my own purposes and thought others might find them useful. This post describes how to extend Texture2D to allow you to rotate it about its center by an arbitrary angle. The Objective-C code is available on github.

Texture2D rotation screenshot

The aim is to extend Texture2D to provide two new methods, where rotationAngle is the angle in degrees, counter-clockwise (or clockwise with a negative angle) to rotate the texture about its center:

- (void) drawInRect:(CGRect)rect rotatedBy:(float)rotationAngle;  
- (void) drawAtPoint:(CGPoint)point rotatedBy:(float)rotationAngle;  

In order to do this, a new co-ordinate needs to be determined for each corner of the bounding rectangle of the Texture2D object. It's much easier to rotate points about the origin than about an arbitrary point, so the approach I take is to translate the bounding rectangle so that its center is at the origin, rotate the corners, then translate the result back so its center is back in its initial position.

Steps to rotate a texture

Translating to the origin

To translate the bounding rectangle so that its center is on the origin, simply subtract the co-ordinates of the center from the co-ordinates of each of the corners.

Rotating about the origin

To rotate each corner around the origin, we need a formula to rotate a point (or, if you prefer, a vector that starts at the origin) about the origin.

Rotating a vector about the origin

Determining values for x' and y':

            x' = r cos(α')  
Substitute: α' = α + θ  
            x' = r cos(α + θ)  
            x' = r(cos(α)cos(θ) - sin(α)sin(θ))  
Substitute: cos(α) = x/r and sin(α) = y/r  
            x' = r(x/r cos(θ) - y/r sin(θ))  
            x' = x cos(θ) - y sin(θ)

Similarly it can be shown that

            y' = x sin(θ) + y cos(θ)

Translating back

To translate the bounding rectangle of the Texture2D object back so that its center is in its initial position, simply add the initial co-ordinates of the center back to the co-ordinates of each of the corners.

Bringing it all together

Bringing the all together, for each corner of the bounding rectangle of a Texture2D object:

Let cx, cy be the center point of the bounding rectangle  
For each corner, with co-ordinate (x, y):  
     x' = cx + ((x - cx)cos(θ) - (y - cy)sin(θ))  
     y' = cy + ((x - cx)sin(θ) + (y - cy)cos(θ))  
cx and cy are simply:  
     cx = (x + width)/2  
     cy = (y + height)/2

The code

#define ROTATE_ABOUT_POINT(x, y, cx, cy, cosTheta, sinTheta) CGPointMake(((((x) - (cx)) * (  
  cosTheta)) - (((y) - (cy)) * (sinTheta))) + (cx), ((((x) - (cx)) * (sinTheta)) + (((y) -  
  (cy)) * (cosTheta))) + (cy));  
    
#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)

- (void) drawInRect:(CGRect)rect rotatedBy:(float)rotationAngle {  
  float radians = DEGREES_TO_RADIANS(rotationAngle);  
  GLfloat cosTheta = cosf(radians);  
  GLfloat sinTheta = sinf(radians);  
  CGPoint center = CGPointMake(rect.origin.x + (rect.size.width / 2),  
                               rect.origin.y + (rect.size.height / 2));  
  CGPoint p1 = ROTATE_ABOUT_POINT(rect.origin.x, rect.origin.y,  
                                  center.x, center.y,  
                                  cosTheta, sinTheta);  
  CGPoint p2 = ROTATE_ABOUT_POINT(rect.origin.x + rect.size.width, rect.origin.y,  
                                  center.x, center.y,  
                                  cosTheta, sinTheta);  
  CGPoint p3 = ROTATE_ABOUT_POINT(rect.origin.x, rect.origin.y + rect.size.height,  
                                  center.x, center.y,  
                                  cosTheta, sinTheta);  
  CGPoint p4 = ROTATE_ABOUT_POINT(rect.origin.x + rect.size.width,  
                                  rect.origin.y + rect.size.height,  
                                  center.x, center.y,  
                                  cosTheta, sinTheta);  
  GLfloat  coordinates[] = {  
    0, self.maxT,  
    self.maxS, self.maxT,  
    0, 0,  
    self.maxS, 0  
  };  
  GLfloat vertices[] = {  
    p1.x, p1.y, 0.0,  
    p2.x, p2.y, 0.0,  
    p3.x, p3.y, 0.0,  
    p4.x, p4.y, 0.0  
  };  
  glBindTexture(GL_TEXTURE_2D, self.name);  
  glVertexPointer(3, GL_FLOAT, 0, vertices);  
  glTexCoordPointer(2, GL_FLOAT, 0, coordinates);  
  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  
}

- (void) drawAtPoint:(CGPoint)point rotatedBy:(float)rotationAngle {  
    [self drawInRect:CGRectMake(point.x - self.imageSize.width / 2, point.y -   
    self.imageSize.height / 2, self.imageSize.width, self.imageSize.height)  
    rotatedBy: rotationAngle];  
}  

I've used a macro to make the code a bit more readable and calculate cos(θ) and sin(θ) each once and pass them into the macro for performance reasons. Have a look at MyTexture2D class in the code on github for a full implementation of this. The project also includes a small iOS demonstration app showing it in action.

Noavatar

Posted Fri 21 Oct 2011 by Michael Patricios

Tags: Development, Objective-C, OpenGL

Comments

  • I found what i need. Texture2D

    Thanks!

    P.S. Add twitter or Facebook like button

Post a comment