opengl

#import


/**

 * @class Panorama View

 * @author Robby Kraft

 * @date 8/24/13

 *

 * @availability iOS (5.0 and later)

 *

 * @discussion a dynamic GLKView with a touch and motion sensor interface to align and immerse the perspective inside an equirectangular panorama projection

 */

@interface PanoramaView : GLKView



-(id) init;  // recommended init method


-(void) draw;  // place in GLKViewController's glkView:drawInRect:


/// set image

-(void) setImage:(UIImage*)image;


/// set image by path or bundle - will check at both

-(void) setImageWithName:(NSString*)fileName;



/* orientation */


/// forward vector axis (into the screen)

@property (nonatomic, readonly) GLKVector3 lookVector;


/// forward horizontal azimuth (-π to π)

@property (nonatomic, readonly) float lookAzimuth;


/// forward vertical altitude (-.5π to .5π)

@property (nonatomic, readonly) float lookAltitude;



// At this point, it's still recommended to activate either OrientToDevice or TouchToPan, not both

//   it's possible to have them simultaneously, but the effect is confusing and disorienting



/// Activates accelerometer + gyro orientation

@property (nonatomic) BOOL orientToDevice;


/// Enables UIPanGestureRecognizer to affect view orientation

@property (nonatomic) BOOL touchToPan;


/// Fixes up-vector during panning. (trade off: no panning past the poles)

//@property (nonatomic) BOOL preventHeadTilt;


/**

 * Align Z coordinate axis (into the screen) to a GLKVector.

 * (due to a fixed up-vector, flipping will occur at the poles)

 *

 * @param GLKVector3 can be non-normalized

 */

-(void) orientToVector:(GLKVector3)vector;


/**

 * Align Z coordinate axis (into the screen) to azimuth and altitude.

 * (due to a fixed up-vector, flipping will occur at the poles)

 *

 * @param Azimuth(-π to π) Altitude(-.5π to .5π)

 */

-(void) orientToAzimuth:(float) azimuth Altitude:(float)altitude;



/*  projection & touches  */



@property (nonatomic, readonly) NSSet *touches;


@property (nonatomic, readonly) NSInteger numberOfTouches;


/// Field of view in DEGREES

@property (nonatomic) float fieldOfView;


/// Enables UIPinchGestureRecognizer to affect FieldOfView

@property (nonatomic) BOOL pinchToZoom;


/// Dynamic overlay of latitude and longitude intersection lines for all touches

@property (nonatomic) BOOL showTouches;


/**

 * Convert a 3D world-coordinate (specified by a vector from the origin) to a 2D on-screen coordinate

 *

 * @param GLKVector3 coordinate location from origin. Use with CGRectContainsPoint( [[UIScreen mainScreen] bounds], screenPoint )

 * @return a screen pixel coordinate representation of a 3D world coordinate

 */

-(CGPoint) screenLocationFromVector:(GLKVector3)vector;


/**

 * Converts a 2D on-screen coordinate to a vector in 3D space pointing out from the origin

 *

 * @param CGPoint screen coordinate

 * @return GLKVector3 vector pointing outward from origin

 */

-(GLKVector3) vectorFromScreenLocation:(CGPoint)point;


/**

 * Converts a 2D on-screen coordinate to a pixel (x,y) of the loaded panorama image

 *

 * @param CGPoint screen coordinate

 * @return CGPoint image coordinate in pixels. If no image, between 0.0 and 1.0

 */

-(CGPoint) imagePixelAtScreenLocation:(CGPoint)point;


/**

 * Hit-detection for all active touches

 *

 * @param CGRect defined in image pixel coordinates

 * @return YES if touch is inside CGRect, NO otherwise

 */

-(BOOL) touchInRect:(CGRect)rect;

@end







//

//  PanoramaView.m

//  Panorama

//

//  Created by Robby Kraft on 8/24/13.

//  Copyright (c) 2013 Robby Kraft. All rights reserved.

//


#import

#import

#import

#import "PanoramaView.h"


#define FPS 60

#define FOV_MIN 1

#define FOV_MAX 155

#define Z_NEAR 0.1f

#define Z_FAR 100.0f


// LINEAR for smoothing, NEAREST for pixelized

#define IMAGE_SCALING GL_LINEAR  // GL_NEAREST, GL_LINEAR


// this appears to be the best way to grab orientation. if this becomes formalized, just make sure the orientations match

#define SENSOR_ORIENTATION [[UIApplication sharedApplication] statusBarOrientation] //enum  1(NORTH)  2(SOUTH)  3(EAST)  4(WEST)


// this really should be included in GLKit

GLKQuaternion GLKQuaternionFromTwoVectors(GLKVector3 u, GLKVector3 v){

    GLKVector3 w = GLKVector3CrossProduct(u, v);

    GLKQuaternion q = GLKQuaternionMake(w.x, w.y, w.z, GLKVector3DotProduct(u, v));

    q.w += GLKQuaternionLength(q);

    return GLKQuaternionNormalize(q);

}


@interface Sphere : NSObject


-(bool) execute;

-(id) init:(GLint)stacks slices:(GLint)slices radius:(GLfloat)radius textureFile:(NSString *)textureFile;

-(void) swapTexture:(NSString*)textureFile;

-(void) swapTextureWithImage:(UIImage*)image;

-(CGPoint) getTextureSize;


@end


@interface PanoramaView (){

    Sphere *sphere, *meridians;

    CMMotionManager *motionManager;

    UIPinchGestureRecognizer *pinchGesture;

    UIPanGestureRecognizer *panGesture;

    GLKMatrix4 _projectionMatrix, _attitudeMatrix, _offsetMatrix;

    float _aspectRatio;

    GLfloat circlePoints[64*3];  // meridian lines

}

@end


@implementation PanoramaView


-(id) init{

// it appears that iOS already automatically does this switch, stored in UIScreen mainscreen bounds

//    CGRect frame = [[UIScreen mainScreen] bounds];

//    if(SENSOR_ORIENTATION == 3 || SENSOR_ORIENTATION == 4){

//        return [self initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.height, frame.size.width)];

//    } else{

//        return [self initWithFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height)];

//    }

    return [self initWithFrame:[[UIScreen mainScreen] bounds]];

}

- (id)initWithFrame:(CGRect)frame{

    EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

    [EAGLContext setCurrentContext:context];

    self.context = context;

    return [self initWithFrame:frame context:context];

}

-(id) initWithFrame:(CGRect)frame context:(EAGLContext *)context{

    self = [super initWithFrame:frame];

    if (self) {

        [self initDevice];

        [self initOpenGL:context];

        sphere = [[Sphere alloc] init:48 slices:48 radius:10.0 textureFile:nil];

        meridians = [[Sphere alloc] init:48 slices:48 radius:8.0 textureFile:@"equirectangular-projection-lines.png"];

    }

    return self;

}

-(void) didMoveToSuperview{

// this breaks MVC, but useful for setting GLKViewController's frame rate

    UIResponder *responder = self;

    while (![responder isKindOfClass:[GLKViewController class]]) {

        responder = [responder nextResponder];

        if (responder == nil){

            break;

        }

    }

    if([responder respondsToSelector:@selector(setPreferredFramesPerSecond:)])

        [(GLKViewController*)responder setPreferredFramesPerSecond:FPS];

}

-(void) initDevice{

    motionManager = [[CMMotionManager alloc] init];

    pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchHandler:)];

    [pinchGesture setEnabled:NO];

    [self addGestureRecognizer:pinchGesture];

    panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandler:)];

    [panGesture setMaximumNumberOfTouches:1];

    [panGesture setEnabled:NO];

    [self addGestureRecognizer:panGesture];

}

-(void)setFieldOfView:(float)fieldOfView{

    _fieldOfView = fieldOfView;

    [self rebuildProjectionMatrix];

}

-(void) setImageWithName:(NSString*)fileName{

    [sphere swapTexture:fileName];

}

-(void) setImage:(UIImage *)image {

    [sphere swapTextureWithImage:image];

}

-(void) setTouchToPan:(BOOL)touchToPan{

    _touchToPan = touchToPan;

    [panGesture setEnabled:_touchToPan];

}

-(void) setPinchToZoom:(BOOL)pinchToZoom{

    _pinchToZoom = pinchToZoom;

    [pinchGesture setEnabled:_pinchToZoom];

}

-(void) setOrientToDevice:(BOOL)orientToDevice{

    _orientToDevice = orientToDevice;

    if(motionManager.isDeviceMotionAvailable){

        if(_orientToDevice)

            [motionManager startDeviceMotionUpdates];

        else

            [motionManager stopDeviceMotionUpdates];

    }

}

#pragma mark- OPENGL

-(void)initOpenGL:(EAGLContext*)context{

    [(CAEAGLLayer*)self.layer setOpaque:NO];

    _aspectRatio = self.frame.size.width/self.frame.size.height;

    _fieldOfView = 45 + 45 * atanf(_aspectRatio); // hell ya

    [self rebuildProjectionMatrix];

    _attitudeMatrix = GLKMatrix4Identity;

    _offsetMatrix = GLKMatrix4Identity;

    [self customGL];

    [self makeLatitudeLines];

}

-(void)rebuildProjectionMatrix{

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();

    GLfloat frustum = Z_NEAR * tanf(_fieldOfView*0.00872664625997);  // pi/180/2

    _projectionMatrix = GLKMatrix4MakeFrustum(-frustum, frustum, -frustum/_aspectRatio, frustum/_aspectRatio, Z_NEAR, Z_FAR);

    glMultMatrixf(_projectionMatrix.m);

    glViewport(0, 0, self.frame.size.width, self.frame.size.height);

    glMatrixMode(GL_MODELVIEW);

}

-(void) customGL{

    glMatrixMode(GL_MODELVIEW);

//    glEnable(GL_CULL_FACE);

//    glCullFace(GL_FRONT);

//    glEnable(GL_DEPTH_TEST);

    glEnable(GL_BLEND);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

}

-(void)draw{

    static GLfloat whiteColor[] = {1.0f, 1.0f, 1.0f, 1.0f};

    static GLfloat clearColor[] = {0.0f, 0.0f, 0.0f, 0.0f};

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    glClear(GL_COLOR_BUFFER_BIT);

    glPushMatrix(); // begin device orientation


        _attitudeMatrix = GLKMatrix4Multiply([self getDeviceOrientationMatrix], _offsetMatrix);

        [self updateLook];

    

        glMultMatrixf(_attitudeMatrix.m);

    

        glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, whiteColor);  // panorama at full color

        [sphere execute];

        glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, clearColor);

//        [meridians execute];  // semi-transparent texture overlay (15° meridian lines)


//TODO: add any objects here to make them a part of the virtual reality

//        glPushMatrix();

//        // object code

//        glPopMatrix();

    

        // touch lines

        if(_showTouches && _numberOfTouches){

            glColor4f(1.0f, 1.0f, 1.0f, 0.5f);

            for(int i = 0; i < [[_touches allObjects] count]; i++){

                glPushMatrix();

                    CGPoint touchPoint = CGPointMake([(UITouch*)[[_touches allObjects] objectAtIndex:i] locationInView:self].x,

                                                     [(UITouch*)[[_touches allObjects] objectAtIndex:i] locationInView:self].y);

                    [self drawHotspotLines:[self vectorFromScreenLocation:touchPoint inAttitude:_attitudeMatrix]];

                glPopMatrix();

            }

            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

        }

    

    glPopMatrix(); // end device orientation

}

#pragma mark- ORIENTATION

-(GLKMatrix4) getDeviceOrientationMatrix{

    if(_orientToDevice && [motionManager isDeviceMotionActive]){

        CMRotationMatrix a = [[[motionManager deviceMotion] attitude] rotationMatrix];

        // arrangements of mappings of sensor axis to virtual axis (columns)

        // and combinations of 90 degree rotations (rows)

        if(SENSOR_ORIENTATION == 4){

            return GLKMatrix4Make( a.m21,-a.m11, a.m31, 0.0f,

                                   a.m23,-a.m13, a.m33, 0.0f,

                                  -a.m22, a.m12,-a.m32, 0.0f,

                                   0.0f , 0.0f , 0.0f , 1.0f);

        }

        if(SENSOR_ORIENTATION == 3){

            return GLKMatrix4Make(-a.m21, a.m11, a.m31, 0.0f,

                                  -a.m23, a.m13, a.m33, 0.0f,

                                   a.m22,-a.m12,-a.m32, 0.0f,

                                   0.0f , 0.0f , 0.0f , 1.0f);

        }

        if(SENSOR_ORIENTATION == 2){

            return GLKMatrix4Make(-a.m11,-a.m21, a.m31, 0.0f,

                                  -a.m13,-a.m23, a.m33, 0.0f,

                                   a.m12, a.m22,-a.m32, 0.0f,

                                   0.0f , 0.0f , 0.0f , 1.0f);

        }

        return GLKMatrix4Make(a.m11, a.m21, a.m31, 0.0f,

                              a.m13, a.m23, a.m33, 0.0f,

                             -a.m12,-a.m22,-a.m32, 0.0f,

                              0.0f , 0.0f , 0.0f , 1.0f);

    }

    else

        return GLKMatrix4Identity;

}

-(void) orientToVector:(GLKVector3)v{

    _attitudeMatrix = GLKMatrix4MakeLookAt(0, 0, 0, v.x, v.y, v.z0, 1, 0);

    [self updateLook];

}

-(void) orientToAzimuth:(float)azimuth Altitude:(float)altitude{

    [self orientToVector:GLKVector3Make(-cosf(azimuth), sinf(altitude), sinf(azimuth))];

}

-(void) updateLook{

    _lookVector = GLKVector3Make(-_attitudeMatrix.m02,

                                 -_attitudeMatrix.m12,

                                 -_attitudeMatrix.m22);

    _lookAzimuth = atan2f(_lookVector.x, -_lookVector.z);

    _lookAltitude = asinf(_lookVector.y);

}

-(CGPoint) imagePixelAtScreenLocation:(CGPoint)point{

    return [self imagePixelFromVector:[self vectorFromScreenLocation:point inAttitude:_attitudeMatrix]];

}

-(CGPoint) imagePixelFromVector:(GLKVector3)vector{

    CGPoint pxl = CGPointMake((M_PI-atan2f(-vector.z, -vector.x))/(2*M_PI), acosf(vector.y)/M_PI);

    CGPoint tex = [sphere getTextureSize];

    // if no texture exists, returns between 0.0 - 1.0

    if(!(tex.x == 0.0f && tex.y == 0.0f)){

        pxl.x *= tex.x;

        pxl.y *= tex.y;

    }

    return pxl;

}

-(GLKVector3) vectorFromScreenLocation:(CGPoint)point{

    return [self vectorFromScreenLocation:point inAttitude:_attitudeMatrix];

}

-(GLKVector3) vectorFromScreenLocation:(CGPoint)point inAttitude:(GLKMatrix4)matrix{

    GLKMatrix4 inverse = GLKMatrix4Invert(GLKMatrix4Multiply(_projectionMatrix, matrix), nil);

    GLKVector4 screen = GLKVector4Make(2.0*(point.x/self.frame.size.width-.5),

                                       2.0*(.5-point.y/self.frame.size.height),

                                       1.0, 1.0);

//    if (SENSOR_ORIENTATION == 3 || SENSOR_ORIENTATION == 4)

//        screen = GLKVector4Make(2.0*(screenTouch.x/self.frame.size.height-.5),

//                                2.0*(.5-screenTouch.y/self.frame.size.width),

//                                1.0, 1.0);

    GLKVector4 vec = GLKMatrix4MultiplyVector4(inverse, screen);

    return GLKVector3Normalize(GLKVector3Make(vec.x, vec.y, vec.z));

}

-(CGPoint) screenLocationFromVector:(GLKVector3)vector{

    GLKMatrix4 matrix = GLKMatrix4Multiply(_projectionMatrix, _attitudeMatrix);

    GLKVector3 screenVector = GLKMatrix4MultiplyVector3(matrix, vector);

    return CGPointMake( (screenVector.x/screenVector.z/2.0 + 0.5) * self.frame.size.width,

                       (0.5-screenVector.y/screenVector.z/2) * self.frame.size.height );

}

-(BOOL) computeScreenLocation:(CGPoint*)location fromVector:(GLKVector3)vector inAttitude:(GLKMatrix4)matrix{

//This method returns whether the point is before or behind the screen.

    GLKVector4 screenVector;

    GLKVector4 vector4;

    if(location == NULL)

        return NO;

    matrix = GLKMatrix4Multiply(_projectionMatrix, matrix);

    vector4 = GLKVector4Make(vector.x, vector.y, vector.z, 1);

    screenVector = GLKMatrix4MultiplyVector4(matrix, vector4);

    location->x = (screenVector.x/screenVector.w/2.0 + 0.5) * self.frame.size.width;

    location->y = (0.5-screenVector.y/screenVector.w/2) * self.frame.size.height;

    return (screenVector.z >= 0);

}

#pragma mark- TOUCHES

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

    _touches = event.allTouches;

    _numberOfTouches = event.allTouches.count;

}

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{

    _touches = event.allTouches;

    _numberOfTouches = event.allTouches.count;

}

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

    _touches = event.allTouches;

    _numberOfTouches = 0;

}

-(BOOL)touchInRect:(CGRect)rect{

    if(_numberOfTouches){

        bool found = false;

        for(int i = 0; i < [[_touches allObjects] count]; i++){

            CGPoint touchPoint = CGPointMake([(UITouch*)[[_touches allObjects] objectAtIndex:i] locationInView:self].x,

                                             [(UITouch*)[[_touches allObjects] objectAtIndex:i] locationInView:self].y);

            found |= CGRectContainsPoint(rect, [self imagePixelAtScreenLocation:touchPoint]);

        }

        return found;

    }

    return false;

}

-(void)pinchHandler:(UIPinchGestureRecognizer*)sender{

    _numberOfTouches = sender.numberOfTouches;

    static float zoom;

    if([sender state] == 1)

        zoom = _fieldOfView;

    if([sender state] == 2){

        CGFloat newFOV = zoom / [sender scale];

        if(newFOV < FOV_MIN) newFOV = FOV_MIN;

        else if(newFOV > FOV_MAX) newFOV = FOV_MAX;

        [self setFieldOfView:newFOV];

    }

    if([sender state] == 3){

        _numberOfTouches = 0;

    }

}

-(void) panHandler:(UIPanGestureRecognizer*)sender{

    static GLKVector3 touchVector;

    if([sender state] == 1){

        touchVector = [self vectorFromScreenLocation:[sender locationInView:sender.view] inAttitude:_offsetMatrix];

    }

    else if([sender state] == 2){

        GLKVector3 nowVector = [self vectorFromScreenLocation:[sender locationInView:sender.view] inAttitude:_offsetMatrix];

        GLKQuaternion q = GLKQuaternionFromTwoVectors(touchVector, nowVector);

        _offsetMatrix = GLKMatrix4Multiply(_offsetMatrix, GLKMatrix4MakeWithQuaternion(q));

        // in progress for preventHeadTilt

//        GLKMatrix4 mat = GLKMatrix4Multiply(_offsetMatrix, GLKMatrix4MakeWithQuaternion(q));

//        _offsetMatrix = GLKMatrix4MakeLookAt(0, 0, 0, -mat.m02, -mat.m12, -mat.m22,  0, 1, 0);

    }

    else{

        _numberOfTouches = 0;

    }

}

#pragma mark- MERIDIANS

-(void) makeLatitudeLines{

    for(int i = 0; i < 64; i++){

        circlePoints[i*3+0] = -sinf(M_PI*2/64.0f*i);

        circlePoints[i*3+1] = 0.0f;

        circlePoints[i*3+2] = cosf(M_PI*2/64.0f*i);

    }

}

-(void)drawHotspotLines:(GLKVector3)touchLocation{

    glLineWidth(2.0f);

    float scale = sqrtf(1-powf(touchLocation.y,2));

    glPushMatrix();

    glScalef(scale, 1.0f, scale);

    glTranslatef(0, touchLocation.y, 0);

    glDisableClientState(GL_NORMAL_ARRAY);

    glEnableClientState(GL_VERTEX_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, circlePoints);

    glDrawArrays(GL_LINE_LOOP, 0, 64);

    glDisableClientState(GL_VERTEX_ARRAY);

    glPopMatrix();

    

    glPushMatrix();

    glRotatef(-atan2f(-touchLocation.z, -touchLocation.x)*180/M_PI, 0, 1, 0);

    glRotatef(90, 1, 0, 0);

    glDisableClientState(GL_NORMAL_ARRAY);

    glEnableClientState(GL_VERTEX_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, circlePoints);

    glDrawArrays(GL_LINE_STRIP, 0, 33);

    glDisableClientState(GL_VERTEX_ARRAY);

    glPopMatrix();

}

-(void) dealloc{

    [EAGLContext setCurrentContext:nil];

}

@end


@interface Sphere (){

//  from Touch Fighter by Apple

//  in Pro OpenGL ES for iOS

//  by Mike Smithwick Jan 2011 pg. 78

    GLKTextureInfo *m_TextureInfo;

    GLfloat *m_TexCoordsData;

    GLfloat *m_VertexData;

    GLfloat *m_NormalData;

    GLint m_Stacks, m_Slices;

    GLfloat m_Scale;

}

-(GLKTextureInfo *) loadTextureFromBundle:(NSString *) filename;

-(GLKTextureInfo *) loadTextureFromPath:(NSString *) path;

-(GLKTextureInfo *) loadTextureFromImage:(UIImage *) image;

@end

@implementation Sphere

-(id) init:(GLint)stacks slices:(GLint)slices radius:(GLfloat)radius textureFile:(NSString *)textureFile{

    // modifications:

    //   flipped(inverted) texture coords across the Z

    //   vertices rotated 90deg

    if(textureFile != nil) m_TextureInfo = [self loadTextureFromBundle:textureFile];

    m_Scale = radius;

    if((self = [super init])){

        m_Stacks = stacks;

        m_Slices = slices;

        m_VertexData = nil;

        m_TexCoordsData = nil;

        // Vertices

        GLfloat *vPtr = m_VertexData = (GLfloat*)malloc(sizeof(GLfloat) * 3 * ((m_Slices*2+2) * (m_Stacks)));

        // Normals

        GLfloat *nPtr = m_NormalData = (GLfloat*)malloc(sizeof(GLfloat) * 3 * ((m_Slices*2+2) * (m_Stacks)));

        GLfloat *tPtr = nil;

        tPtr = m_TexCoordsData = (GLfloat*)malloc(sizeof(GLfloat) * 2 * ((m_Slices*2+2) * (m_Stacks)));

        unsigned int phiIdx, thetaIdx;

        // Latitude

        for(phiIdx = 0; phiIdx < m_Stacks; phiIdx++){

            //starts at -pi/2 goes to pi/2

            //the first circle

            float phi0 = M_PI * ((float)(phiIdx+0) * (1.0/(float)(m_Stacks)) - 0.5);

            //second one

            float phi1 = M_PI * ((float)(phiIdx+1) * (1.0/(float)(m_Stacks)) - 0.5);

            float cosPhi0 = cos(phi0);

            float sinPhi0 = sin(phi0);

            float cosPhi1 = cos(phi1);

            float sinPhi1 = sin(phi1);

            float cosTheta, sinTheta;

            //longitude

            for(thetaIdx = 0; thetaIdx < m_Slices; thetaIdx++){

                float theta = -2.0*M_PI * ((float)thetaIdx) * (1.0/(float)(m_Slices - 1));

                cosTheta = cos(theta+M_PI*.5);

                sinTheta = sin(theta+M_PI*.5);

                //get x-y-x of the first vertex of stack

                vPtr[0] = m_Scale*cosPhi0 * cosTheta;

                vPtr[1] = m_Scale*sinPhi0;

                vPtr[2] = m_Scale*(cosPhi0 * sinTheta);

                //the same but for the vertex immediately above the previous one.

                vPtr[3] = m_Scale*cosPhi1 * cosTheta;

                vPtr[4] = m_Scale*sinPhi1;

                vPtr[5] = m_Scale*(cosPhi1 * sinTheta);

                nPtr[0] = cosPhi0 * cosTheta;

                nPtr[1] = sinPhi0;

                nPtr[2] = cosPhi0 * sinTheta;

                nPtr[3] = cosPhi1 * cosTheta;

                nPtr[4] = sinPhi1;

                nPtr[5] = cosPhi1 * sinTheta;

                if(tPtr!=nil){

                    GLfloat texX = (float)thetaIdx * (1.0f/(float)(m_Slices-1));

                    tPtr[0] = 1.0-texX;

                    tPtr[1] = (float)(phiIdx + 0) * (1.0f/(float)(m_Stacks));

                    tPtr[2] = 1.0-texX;

                    tPtr[3] = (float)(phiIdx + 1) * (1.0f/(float)(m_Stacks));

                }

                vPtr += 2*3;

                nPtr += 2*3;

                if(tPtr != nil) tPtr += 2*2;

            }

            //Degenerate triangle to connect stacks and maintain winding order

            vPtr[0] = vPtr[3] = vPtr[-3];

            vPtr[1] = vPtr[4] = vPtr[-2];

            vPtr[2] = vPtr[5] = vPtr[-1];

            nPtr[0] = nPtr[3] = nPtr[-3];

            nPtr[1] = nPtr[4] = nPtr[-2];

            nPtr[2] = nPtr[5] = nPtr[-1];

            if(tPtr != nil){

                tPtr[0] = tPtr[2] = tPtr[-2];

                tPtr[1] = tPtr[3] = tPtr[-1];

            }

        }

    }

    return self;

}

-(void) dealloc{

    GLuint name = m_TextureInfo.name;

    glDeleteTextures(1, &name);

    

    if(m_TexCoordsData != nil){

        free(m_TexCoordsData);

    }

    if(m_NormalData != nil){

        free(m_NormalData);

    }

    if(m_VertexData != nil){

        free(m_VertexData);

    }

}

-(bool) execute{

    glEnableClientState(GL_NORMAL_ARRAY);

    glEnableClientState(GL_VERTEX_ARRAY);

    if(m_TexCoordsData != nil){

        glEnable(GL_TEXTURE_2D);

        glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        if(m_TextureInfo != 0)

            glBindTexture(GL_TEXTURE_2D, m_TextureInfo.name);

        glTexCoordPointer(2, GL_FLOAT, 0, m_TexCoordsData);

    }

    glVertexPointer(3, GL_FLOAT, 0, m_VertexData);

    glNormalPointer(GL_FLOAT, 0, m_NormalData);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, (m_Slices +1) * 2 * (m_Stacks-1)+2);

    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    glDisable(GL_TEXTURE_2D);

    glDisableClientState(GL_VERTEX_ARRAY);

    glDisableClientState(GL_NORMAL_ARRAY);

    return true;

}

-(GLKTextureInfo *) loadTextureFromBundle:(NSString *) filename{

    if(!filename) return nil;

    NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:NULL];

    return [self loadTextureFromPath:path];

}

-(GLKTextureInfo *) loadTextureFromPath:(NSString *) path{

    if(!path) return nil;

    NSError *error;

    GLKTextureInfo *info;

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], GLKTextureLoaderOriginBottomLeft, nil];

    info=[GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];

    glBindTexture(GL_TEXTURE_2D, info.name);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, IMAGE_SCALING);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, IMAGE_SCALING);

    return info;

}

-(GLKTextureInfo *) loadTextureFromImage:(UIImage *) image {

    if(!image) return nil;

    NSError *error;

    GLKTextureInfo *info;

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],GLKTextureLoaderOriginBottomLeft, nil];

    info = [GLKTextureLoader textureWithCGImage:image.CGImage options:options error:&error];

    glBindTexture(GL_TEXTURE_2D, info.name);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, IMAGE_SCALING);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, IMAGE_SCALING);

    return info;

}

-(void)swapTexture:(NSString*)textureFile{

    GLuint name = m_TextureInfo.name;

    glDeleteTextures(1, &name);

    if ([[NSFileManager defaultManager] fileExistsAtPath:textureFile]) {

        m_TextureInfo = [self loadTextureFromPath:textureFile];

    }

    else {

        m_TextureInfo = [self loadTextureFromBundle:textureFile];

    }

}

-(void)swapTextureWithImage:(UIImage*)image {

    GLuint name = m_TextureInfo.name;

    glDeleteTextures(1, &name);

    m_TextureInfo = [self loadTextureFromImage:image];

}

-(CGPoint)getTextureSize{

    if(m_TextureInfo){

        return CGPointMake(m_TextureInfo.width, m_TextureInfo.height);

    }

    else{

        return CGPointZero;

    }

}


@end


你可能感兴趣的:(opengl)