// Copyright (C) 2005 Dave Griffiths
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

// Needs OpenGL, GLU and glut installed
// To compile:
// g++ nurbshowto.cpp -o nurbshowto -lGL -lGLU -lglut

#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>


// our globals
const int order=4;         // make a cubic spline
const int cvcount=10;      // number of cvs
const int stride=3;        // just refers to the number of float values in each cv 
const int numknots=cvcount+order;
const int numcvs=cvcount*stride;
float knots[numknots];     // somewhere to keep the knots
float cvs[numcvs];         // somewhere to keep the cvs
int frame=0;               // for cheezy animation purposes

GLUnurbsObj *mynurbs=NULL; // the nurbs tesselator (or rather a pointer to what will be it)

// setup the GL state - just gets run once at the start
void Setup()
{	
	// setup a perspective projection
    glMatrixMode (GL_PROJECTION);
  	glLoadIdentity();
	glFrustum(-1,1,-0.75,0.75,1,100);
	
	// set the modelview matrix
	glMatrixMode (GL_MODELVIEW);
  	glLoadIdentity();
	
	// set point size and colour for the cv rendering
	glPointSize(4);
	glColor3f(0,0,1);
	
	// set the clear colour to something other than black
	glClearColor(0.5,0.5,0.5,1);
	
	// no lighting needed
	glDisable(GL_LIGHTING);
	
	// position the camera in the world
	glTranslatef(0,0,-1);
	glRotatef(-90,1,0,0);
	glRotatef(90,0,0,1);
	
	// now, make the nurbs tesselator we are going to use
	mynurbs = gluNewNurbsRenderer();
	
	// these lines set how to tesselate the curve - 
	// simply by chopping it up into 100 line segments
	gluNurbsProperty(mynurbs, GLU_SAMPLING_METHOD, GLU_DOMAIN_DISTANCE);
	gluNurbsProperty(mynurbs, GLU_U_STEP, 100);
	
	// set up the knot vector - make it continuous
	for (int knot=0; knot<numknots; knot++) 
	{
		knots[knot]=knot;
	}	
}

// called once per frame before render, just change our cvs with a silly function
void Update()
{
	int count=0;	
	for (int u=0; u<cvcount; u++)
	{
		cvs[count++]=0;
		cvs[count++]=cos(5.2*sqrt(u*u)+frame*0.01)*0.5;
		cvs[count++]=sin(10*sqrt(u*u)+frame*0.013)*0.5;
	}
	frame++;
}

// called once per frame to show the surface
void Render()
{
	glColor3f(0,0,0);

	// tells GLU we are going to describe a nurbs curve
	gluBeginCurve(mynurbs);
	
	// send it the definition and pointers to cv and knot data
	gluNurbsCurve(mynurbs,numknots,knots,stride,cvs,order,GL_MAP1_VERTEX_3);

	// thats all...
	gluEndCurve(mynurbs);
	
	glColor3f(0,0,1);
	// render the control vertex positions
	glBegin(GL_POINTS);
	for (int n=0; n<cvcount; n++)
	{
		glVertex3fv(&cvs[n*3]);
	}
	glEnd();
}

void DisplayCallback()
{
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	Update();
	Render();
	glutSwapBuffers();
}

void IdleCallback()
{
	glutPostRedisplay();
}

int main(int argc, char *argv[])
{		
	glutInitWindowSize(720,576) ;
  	glutInit(&argc,argv);
	glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
  	glutCreateWindow("nurbshowto");
	glutDisplayFunc(DisplayCallback);
	glutIdleFunc(IdleCallback);
	
	Setup();
	
	glutMainLoop();
  	
	// cleans up the tesselator
	gluDeleteNurbsRenderer(mynurbs);

	return 0;
}



