/*
This file demonstrates how to stream live video from a video capture 
device to an OpenGL texture using OpenCV and GLUT. 
AVI files can also be used (see line 124). It compiles on a GNU/Linux system.

Why? OpenCV offers advanced texture processing and analysis: being able 
to find natural features in images on openGL surfaces offers up many 
interesting possibilities.

Copyright (C) 2007 Julian Oliver
http://julianoliver.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <GL/freeglut.h>
#include <GL/gl.h>
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <ctype.h>

int height, width, count, init;
float fps, rot;

CvCapture *capture = 0; 
IplImage *image;
GLenum format;
GLuint imageID;

#define IsRGB(s) ((s[0] == 'R') && (s[1] == 'G') && (s[2] == 'B'))
#define IsBGR(s) ((s[0] == 'B') && (s[1] == 'G') && (s[2] == 'R'))

#ifndef GL_CLAMP_TO_BORDER
#define GL_CLAMP_TO_BORDER 0x812D
#endif
#define GL_MIRROR_CLAMP_EXT 0x8742

void glutReshape(int width,int height)
{
    int winWidth  = 640; 
    int winHeight = 480;
    glViewport(0,0,width,height);

    // set projection matrix
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0f,(GLfloat)width/(GLfloat)height,0.1f,200.0f);

    // switch back to modelview matrix
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
	
void draw()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    IplImage *frame = 0;
    frame = cvQueryFrame( capture );
    image = cvCreateImage(cvSize(512, 512), 8, 3); 
    cvResize(frame, image, 0); // have to scale to power of two. will work out padding to avoid scale distortion later.
    image->origin = frame->origin;
    //cvSaveImage("foo.png",image); // write out a frame as image file 
    glLoadIdentity();
    glTranslatef(0.0f, 0.0f,-1.0f);
    rot +=1;
    glRotatef(rot, 0, 1, 1); // rotate the matrix to rotate the video texture.

    GLenum format = IsBGR(image->channelSeq) ? GL_BGR_EXT : GL_RGBA;

    // upload to openGL texture
    if (!init) {
      glGenTextures(1, &imageID);
      glBindTexture(GL_TEXTURE_2D, imageID);
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      //GL_LINEAR is better looking than GL_NEAREST but seems slower..
      //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, format, GL_UNSIGNED_BYTE, image->imageData);
      init= 1; // init is over
    }
    else {
      glBindTexture(GL_TEXTURE_2D, imageID);
      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height, format, GL_UNSIGNED_BYTE, image->imageData); 
    }

    glEnable(GL_TEXTURE_2D);
    // make a quad
    glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex3f(-.5, -.5, 0);
        glTexCoord2f(1, 0); glVertex3f(.5, -.5, 0);
        glTexCoord2f(1, 1); glVertex3f(.5, .5, 0);
        glTexCoord2f(0, 1); glVertex3f(-.5, .5, 0);
    glEnd();
    glLoadIdentity();
    glutSwapBuffers();// swap the draw buffer
    cvReleaseImage(&image); // cleanup used image
    glFlush();
}


void exitGL(int returnCode)
{
    exit(returnCode);
}

void glutKeyboard(unsigned char key,int x,int y)
{
    if (key == 27) exitGL(0);
}

int main( int argc, char** argv )
{
    capture = cvCaptureFromCAM(0);
    //capture = cvCaptureFromAVI("myhomemovie.avi"); // non-live video textures work too (will crash on EOF though..)
    glutInit(&argc,argv);
    glutInitWindowPosition(0,0);
    glutInitWindowSize(640,480);
    //glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH);
    glutCreateWindow("foo");
    glutReshapeFunc(glutReshape);
    glutKeyboardFunc(glutKeyboard);
    glutDisplayFunc(draw);
    glutIdleFunc(draw);
    glutMainLoop();
    return 0;
}

