#import "ES2Renderer.h"

// uniform index
enum {
    UNIFORM_MODELVIEWMATRIX,
    UNIFORM_TEXTURE,
    NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];

// attribute index
enum {
    ATTRIB_VERTEX,
    ATTRIB_TEXTUREPOSITION,
    NUM_ATTRIBUTES
};

@interface ES2Renderer (PrivateMethods)
- (BOOL)loadShaders;
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file;
- (BOOL)linkProgram:(GLuint)prog;
- (BOOL)validateProgram:(GLuint)prog;
@end

AVAudioPlayer *audioPlayer;
BOOL stopRendering = NO;

@implementation ES2Renderer

@synthesize outputTexture;
@synthesize newFrameAvailableBlock;

extern BOOL camMode;
extern BOOL shutting;

GPUImageMovie *movieFile;

-(NSTimeInterval)t
{
    return [audioPlayer deviceCurrentTime]*0.3;
}

-(void)playAudio
{
    [audioPlayer play];
}
-(void)stopAudio
{
    [audioPlayer stop];
}

-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
    [audioPlayer stop];
    stopRendering = YES;
}
-(void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
}
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
}
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player
{
}


- (id)initWithSize:(CGSize)newSize;
{
    if ((self = [super init]))
    {
        // Need to use a share group based on the GPUImage context to share textures with the 3-D scene
        context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:[[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context] sharegroup]];

        if (!context || ![EAGLContext setCurrentContext:context] || ![self loadShaders])
        {
            [self release];
            return nil;
        }
        
        backingWidth = (int)newSize.width;
        backingHeight = (int)newSize.height;
		
		currentCalculatedMatrix = CATransform3DIdentity;
		currentCalculatedMatrix = CATransform3DScale(currentCalculatedMatrix, 0.5, 0.5 * (320.0/480.0), 0.5);
        
        glActiveTexture(GL_TEXTURE0);
        glGenTextures(1, &outputTexture);
        glBindTexture(GL_TEXTURE_2D, outputTexture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        // This is necessary for non-power-of-two textures
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glBindTexture(GL_TEXTURE_2D, 0);

        glActiveTexture(GL_TEXTURE1);
        glGenFramebuffers(1, &defaultFramebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
        
        glBindTexture(GL_TEXTURE_2D, outputTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, backingWidth, backingHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTexture, 0);
        
//        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
//        
//        NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @"Incomplete filter FBO: %d", status);
        glBindTexture(GL_TEXTURE_2D, 0);
        
        NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
                                             pathForResource:@"video"
                                             ofType:@"mp4"]];
        
        movieFile = [[GPUImageMovie alloc] initWithURL:url];
        
//        videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
//        videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
        
        //    filter = [[GPUImagePixellateFilter alloc] init];
        //    [(GPUImagePixellateFilter *)filter setFractionalWidthOfAPixel:0.01];
        
        //    filter = [[GPUImageGaussianBlurFilter alloc] init];
        //    [(GPUImageGaussianBlurFilter *)filter setBlurSize:3.0];

        inputFilter2 = [[GPUImagePixellateFilter alloc] init];
            [(GPUImagePixellateFilter *)inputFilter2 setFractionalWidthOfAPixel:0.01];

        textureOutput = [[GPUImageTextureOutput alloc] init];
        textureOutput.delegate = self;
        
        [movieFile addTarget:inputFilter2];
        [inputFilter2 addTarget:textureOutput];
        
        NSError *error;
        
        NSURL *s_url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
                                             pathForResource:@"soundtrack"
                                             ofType:@"mp3"]];
        
        audioPlayer = [[AVAudioPlayer alloc]
                       initWithContentsOfURL:s_url
                       error:&error];
        
        if (error)
        {
            NSLog(@"Error in audioPlayer: %@", 
                  [error localizedDescription]);
        } else {
            audioPlayer.delegate = self;
            [audioPlayer prepareToPlay];
        }
        
        [movieFile startProcessing];
        [self playAudio];

    }

    return self;
}

- (id)initWithSize_cam:(CGSize)newSize;
{
    if ((self = [super init]))
    {
        // Need to use a share group based on the GPUImage context to share textures with the 3-D scene
        context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:[[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context] sharegroup]];
        
        if (!context || ![EAGLContext setCurrentContext:context] || ![self loadShaders])
        {
            [self release];
            return nil;
        }
        
        backingWidth = (int)newSize.width;
        backingHeight = (int)newSize.height;
		
		currentCalculatedMatrix = CATransform3DIdentity;
		currentCalculatedMatrix = CATransform3DScale(currentCalculatedMatrix, 0.5, 0.5 * (320.0/480.0), 0.5);
        
        glActiveTexture(GL_TEXTURE0);
        glGenTextures(1, &outputTexture);
        glBindTexture(GL_TEXTURE_2D, outputTexture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        // This is necessary for non-power-of-two textures
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glBindTexture(GL_TEXTURE_2D, 0);
        
        glActiveTexture(GL_TEXTURE1);
        glGenFramebuffers(1, &defaultFramebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
        
        glBindTexture(GL_TEXTURE_2D, outputTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, backingWidth, backingHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTexture, 0);
        
        //        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        //        
        //        NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @"Incomplete filter FBO: %d", status);
        glBindTexture(GL_TEXTURE_2D, 0);
        
        
                videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
                videoCamera.outputImageOrientation = UIInterfaceOrientationLandscapeLeft;
        
        //    filter = [[GPUImagePixellateFilter alloc] init];
        //    [(GPUImagePixellateFilter *)filter setFractionalWidthOfAPixel:0.01];
        
        //    filter = [[GPUImageGaussianBlurFilter alloc] init];
        //    [(GPUImageGaussianBlurFilter *)filter setBlurSize:3.0];
        
        
        inputFilter2 = [[GPUImagePixellateFilter alloc] init];
        [(GPUImagePixellateFilter *)inputFilter2 setFractionalWidthOfAPixel:0.01];
        
        textureOutput = [[GPUImageTextureOutput alloc] init];
        textureOutput.delegate = self;
        
        [videoCamera addTarget:inputFilter2];
        [inputFilter2 addTarget:textureOutput];
        
        [videoCamera startCameraCapture];
    }
    
    return self;
}


- (void)renderByRotatingAroundX:(float)xRotation rotatingAroundY:(float)yRotation zPosition:(float)zpos;
{
    static const GLfloat cubeVertices[] = { 
        -1.0, -1.0, -1.0, // 0
        1.0,  1.0, -1.0, // 2
        1.0, -1.0, -1.0, // 1

        -1.0, -1.0, -1.0, // 0
        -1.0,  1.0, -1.0, // 3
        1.0,  1.0, -1.0, // 2

        1.0, -1.0, -1.0, // 1
        1.0,  1.0, -1.0, // 2
        1.0,  1.0,  1.0, // 6

        1.0,  1.0,  1.0, // 6
        1.0, -1.0,  1.0, // 5
        1.0, -1.0, -1.0, // 1

        -1.0, -1.0,  1.0, // 4
        1.0, -1.0,  1.0, // 5
        1.0,  1.0,  1.0, // 6

        1.0,  1.0,  1.0, // 6
        -1.0,  1.0,  1.0,  // 7
        -1.0, -1.0,  1.0, // 4

        1.0,  1.0, -1.0, // 2
        -1.0,  1.0, -1.0, // 3
        1.0,  1.0,  1.0, // 6

        1.0,  1.0,  1.0, // 6
        -1.0,  1.0, -1.0, // 3
        -1.0,  1.0,  1.0,  // 7

        -1.0, -1.0, -1.0, // 0
        -1.0,  1.0,  1.0,  // 7
        -1.0,  1.0, -1.0, // 3

        -1.0, -1.0, -1.0, // 0
        -1.0, -1.0,  1.0, // 4
        -1.0,  1.0,  1.0,  // 7

        -1.0, -1.0, -1.0, // 0
        1.0, -1.0, -1.0, // 1
        1.0, -1.0,  1.0, // 5

        -1.0, -1.0, -1.0, // 0
        1.0, -1.0,  1.0, // 5
        -1.0, -1.0,  1.0 // 4
    };  

	const GLfloat cubeTexCoords[] = {
        0.0, 0.0,
        1.0, 1.0,
        1.0, 0.0,
        
        0.0, 0.0,
        0.0, 1.0,
        1.0, 1.0,
        
        0.0, 0.0,
        0.0, 1.0,
        1.0, 1.0,
        
        1.0, 1.0,
        1.0, 0.0,
        0.0, 0.0,

        1.0, 0.0,
        0.0, 0.0,
        0.0, 1.0,
        
        0.0, 1.0,
        1.0, 1.0,
        1.0, 0.0,
        
        0.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        
        0.0, 0.0,
        1.0, 1.0,
        1.0, 0.0,
        
        1.0, 0.0,
        0.0, 1.0,
        1.0, 1.0,
        
        1.0, 0.0,
        0.0, 0.0,
        0.0, 1.0,
        
        0.0, 1.0,
        1.0, 1.0,
        1.0, 0.0,
        
        0.0, 1.0,
        1.0, 0.0,
        0.0, 0.0
        

    };
	
    currentCalculatedMatrix = CATransform3DIdentity;
    
	// Perform incremental rotation based on current angles in X and Y	
	if ((xRotation != 0.0) || (yRotation != 0.0))
	{
		GLfloat totalRotation = sqrt(xRotation*xRotation + yRotation*yRotation);
		
		CATransform3D temporaryMatrix = CATransform3DRotate(currentCalculatedMatrix, totalRotation * M_PI / 180.0, 
															((xRotation/totalRotation) * currentCalculatedMatrix.m12 + (yRotation/totalRotation) * currentCalculatedMatrix.m11),
															((xRotation/totalRotation) * currentCalculatedMatrix.m22 + (yRotation/totalRotation) * currentCalculatedMatrix.m21),
															((xRotation/totalRotation) * currentCalculatedMatrix.m32 + (yRotation/totalRotation) * currentCalculatedMatrix.m31));
		if ((temporaryMatrix.m11 >= -100.0) && (temporaryMatrix.m11 <= 100.0))
			currentCalculatedMatrix = temporaryMatrix;
        
        temporaryMatrix = CATransform3DTranslate(temporaryMatrix, 0, 0, zpos);
	}
	else
	{
	}
	
	GLfloat currentModelViewMatrix[16];
	

	[self convert3DTransform:&currentCalculatedMatrix toMatrix:currentModelViewMatrix];
    
    glActiveTexture(GL_TEXTURE4);
	glBindTexture(GL_TEXTURE_2D, textureForCubeFace);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	
    // Update uniform value
	glUniform1i(uniforms[UNIFORM_TEXTURE], 4);
	glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWMATRIX], 1, 0, currentModelViewMatrix);

    // Update attribute values
    glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, 0, 0, cubeVertices);
    glEnableVertexAttribArray(ATTRIB_VERTEX);
	glVertexAttribPointer(ATTRIB_TEXTUREPOSITION, 2, GL_FLOAT, 0, 0, cubeTexCoords);
	glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITION);

	glDrawArrays(GL_TRIANGLES, 0, 36);
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
    GLint status;
    const GLchar *source;

    source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String];
    if (!source)
    {
        NSLog(@"Failed to load vertex shader");
        return FALSE;
    }

    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);

#if defined(DEBUG)
    GLint logLength;
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetShaderInfoLog(*shader, logLength, &logLength, log);
        NSLog(@"Shader compile log:\n%s", log);
        free(log);
    }
#endif

    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    if (status == 0)
    {
        glDeleteShader(*shader);
        return FALSE;
    }

    return TRUE;
}

- (BOOL)linkProgram:(GLuint)prog
{
    GLint status;

    glLinkProgram(prog);

#if defined(DEBUG)
    GLint logLength;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program link log:\n%s", log);
        free(log);
    }
#endif

    glGetProgramiv(prog, GL_LINK_STATUS, &status);
    if (status == 0)
        return FALSE;

    return TRUE;
}

- (BOOL)validateProgram:(GLuint)prog
{
    GLint logLength, status;

    glValidateProgram(prog);
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program validate log:\n%s", log);
        free(log);
    }

    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    if (status == 0)
        return FALSE;

    return TRUE;
}

- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    NSString *vertShaderPathname, *fragShaderPathname;

    // Create shader program
    program = glCreateProgram();

    // Create and compile vertex shader
    vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname])
    {
        NSLog(@"Failed to compile vertex shader");
        return FALSE;
    }

    // Create and compile fragment shader
    fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"];
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname])
    {
        NSLog(@"Failed to compile fragment shader");
        return FALSE;
    }

    // Attach vertex shader to program
    glAttachShader(program, vertShader);

    // Attach fragment shader to program
    glAttachShader(program, fragShader);

    // Bind attribute locations
    // this needs to be done prior to linking
    glBindAttribLocation(program, ATTRIB_VERTEX, "position");
    glBindAttribLocation(program, ATTRIB_TEXTUREPOSITION, "inputTextureCoordinate");

    // Link program
    if (![self linkProgram:program])
    {
        NSLog(@"Failed to link program: %d", program);

        if (vertShader)
        {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader)
        {
            glDeleteShader(fragShader);
            fragShader = 0;
        }
        if (program)
        {
            glDeleteProgram(program);
            program = 0;
        }
        
        return FALSE;
    }

    // Get uniform locations
    uniforms[UNIFORM_MODELVIEWMATRIX] = glGetUniformLocation(program, "modelViewProjMatrix");
    uniforms[UNIFORM_TEXTURE] = glGetUniformLocation(program, "texture");

    // Release vertex and fragment shaders
    if (vertShader)
        glDeleteShader(vertShader);
    if (fragShader)
        glDeleteShader(fragShader);

    return TRUE;
}

- (void)dealloc
{
    // Tear down GL
    if (defaultFramebuffer)
    {
        glDeleteFramebuffers(1, &defaultFramebuffer);
        defaultFramebuffer = 0;
    }

    if (colorRenderbuffer)
    {
        glDeleteRenderbuffers(1, &colorRenderbuffer);
        colorRenderbuffer = 0;
    }

    if (program)
    {
        glDeleteProgram(program);
        program = 0;
    }

    
    [textureOutput endProcessing];
    [movieFile endProcessing];

    // Tear down context
    if ([EAGLContext currentContext] == context)
        [EAGLContext setCurrentContext:nil];
    
    [context release];
    context = nil;

    [super dealloc];
}

- (void)convert3DTransform:(CATransform3D *)transform3D toMatrix:(GLfloat *)matrix;
{
	//	struct CATransform3D
	//	{
	//		CGFloat m11, m12, m13, m14;
	//		CGFloat m21, m22, m23, m24;
	//		CGFloat m31, m32, m33, m34;
	//		CGFloat m41, m42, m43, m44;
	//	};
	
	matrix[0] = (GLfloat)transform3D->m11;
	matrix[1] = (GLfloat)transform3D->m12;
	matrix[2] = (GLfloat)transform3D->m13;
	matrix[3] = (GLfloat)transform3D->m14;
	matrix[4] = (GLfloat)transform3D->m21;
	matrix[5] = (GLfloat)transform3D->m22;
	matrix[6] = (GLfloat)transform3D->m23;
	matrix[7] = (GLfloat)transform3D->m24;
	matrix[8] = (GLfloat)transform3D->m31;
	matrix[9] = (GLfloat)transform3D->m32;
	matrix[10] = (GLfloat)transform3D->m33;
	matrix[11] = (GLfloat)transform3D->m34;
	matrix[12] = (GLfloat)transform3D->m41;
	matrix[13] = (GLfloat)transform3D->m42;
	matrix[14] = (GLfloat)transform3D->m43;
	matrix[15] = (GLfloat)transform3D->m44;
}

#pragma mark -
#pragma mark GPUImageTextureOutputDelegate delegate method

// RENDAAJA

- (void)newFrameReadyFromTextureOutput:(GPUImageTextureOutput *)callbackTextureOutput;
{
    if (shutting) return;
    if (stopRendering) {
        [EAGLContext setCurrentContext:context];
        
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
        
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);
        
        glViewport(0, 0, backingWidth, backingHeight);
        return;   
    }
    
    textureForCubeFace = callbackTextureOutput.texture;
        
    [EAGLContext setCurrentContext:context];
    
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
	
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
    
    glViewport(0, 0, backingWidth, backingHeight);
    
    //glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    //glClear(GL_COLOR_BUFFER_BIT);
    
    glUseProgram(program);	
        
    glEnable(GL_BLEND); // Turn on blending
    //NSLog(@"t: %f", [self t]);
    
    if (camMode == NO) {
    
    if ([self t] < 20) {
    
    glBlendFunc(GL_SRC_COLOR, GL_DST_ALPHA);

    } else if ([self t] >= 20 && [self t] < 40) {
        glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
    } else if ([self t] >= 40 && [self t] < 42.5) {
        glBlendFunc(GL_SRC_COLOR, GL_DST_ALPHA);
    } else if ([self t] >= 42.5 && [self t] < 60) {
        glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
    }
    
    for (int i = 0; i < 5; i++) {
        [self renderByRotatingAroundX:[self t]*5*tan(sin([self t]*0.1+i+[self t])) rotatingAroundY:[self t]*5*cos(cos([self t]*0.015+i+[self t]))*0.9 zPosition:-(float)cos(i+[self t]*0.1)*0.1];
    }
    if ([self t] < 20) {
        glBlendFunc(GL_ONE_MINUS_SRC_COLOR, GL_DST_ALPHA);
    } else if ([self t] >= 20 && [self t] < 40) {
        glBlendFunc(GL_SRC_COLOR, GL_DST_ALPHA);
    } else if ([self t] >= 40 && [self t] < 42.5) {
        glBlendFunc(GL_ONE_MINUS_SRC_COLOR, GL_DST_ALPHA);
    } else if ([self t] >= 42.5 && [self t] < 60) {
        glBlendFunc(GL_SRC_COLOR, GL_DST_ALPHA);
    }
    
    for (int i = 5; i < 10; i++) {
        [self renderByRotatingAroundX:[self t]*5*cos(sin([self t]*0.1+i+[self t])) rotatingAroundY:[self t]*5*tan(cos([self t]*0.015+i+[self t]))*0.9 zPosition:-(float)cos(i+[self t]*0.1)];
    }
        
    } else {
        
            glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
        
        for (int i = 0; i < 5; i++) {
            [self renderByRotatingAroundX:[self t]*5*tan(sin([self t]*0.1+i+[self t])) rotatingAroundY:[self t]*5*cos(cos([self t]*0.015+i+[self t]))*0.9 zPosition:-(float)cos(i+[self t]*0.1)*0.1];
        }
            glBlendFunc(GL_SRC_COLOR, GL_DST_ALPHA);
        
        for (int i = 5; i < 10; i++) {
            [self renderByRotatingAroundX:[self t]*5*cos(sin([self t]*0.1+i+[self t])) rotatingAroundY:[self t]*5*tan(cos([self t]*0.015+i+[self t]))*0.9 zPosition:-(float)cos(i+[self t]*0.1)];
        }
        
        
    }
    

    glDisable(GL_BLEND); // Turn off blending

    // The flush is required at the end here to make sure the FBO texture is written to before passing it back to GPUImage
    glFlush();
    
	if (shutting == NO) newFrameAvailableBlock();
}

@end
