/*
 * Visual effects for the 4K intro.
 *
 * Coded by:
 *  Polaris
 *  s_tec
 */

// note - some edges are a bit rought - but it shows how to generate an object, as well as 
// that it's better to use the general quadric functions where possible. 

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "screen.h"

// tweak options, used to test sizes
#define textures
#define quadric
#define sphere

// Global variables 
#ifdef textures
	GLuint	gTexture[1];					// Storage For One Texture
	GLubyte gTextureData[256*256*3];		// 256x256xRGB
#endif

#ifdef sphere 
	#ifdef quadric
		GLUquadricObj *gObj;				// quadric sphere	
	#else
		float const PI = 3.1415926535897932384626433832795f;
		float const PI2 = 6.283185307179586476925286766559f;
	#endif 
#endif 

#ifdef sphere
#ifndef quadric 
_inline void custom_sphere()
{
	// configurable params
	float R=0.5f;				// default radius value
	float quality = 50.0f;		// 50 polys go around the circle
	float deltaA=PI2/quality;	// delta angle per poly range
	float xoffA=0;				// offset of the point in the square xAngle
	float yoffA=0;				// offset of the point in the square yAngle
	float yaxisA,xaxisA;		// foats 
	float x,y,z;				// eventual real point;
	int i;

	glBindTexture(GL_TEXTURE_2D, gTexture[0]);	
	
	for (yaxisA=0;yaxisA<PI2;yaxisA+=deltaA*2)
	for (xaxisA=deltaA;xaxisA<PI;xaxisA+=deltaA)
	{
		// square points 
		// a-----b	// a = upper left  (for central square closest to viewer, looking down Z)
		// |     |  // b = upper right
		// |     |  // c = lower right
		// d-----c  // d = lower left

		glBegin(GL_QUADS); // Draw A Quad
		for (i=0;i<4;i++) // iterate across all for points
		{
			// populate offset values
			// optimization task - group case statement to take advantage of 
			// symmetry 
			switch(i) {
			case 0:
				// upper left
				xoffA=-deltaA;
				yoffA=-deltaA;
				break;
			case 1:
				// upper right
				xoffA=-deltaA;
				yoffA=deltaA;
				break;
			case 2:
				// lower right
				xoffA=deltaA;
				yoffA=deltaA;
				break;
			case 3:
				// lower left
				xoffA=deltaA;
				yoffA=-deltaA;
				break;
			
			}// end switch

			// draw all 4 points
			// todo: xaxisA+xoffA and yaxisA+yoffA can be easily reduced. 
			
			// rotate X axis (draw squares Zenith to Nadir)
			// initial point <0,R,0>

				//New_Y = y * cos(x_angle) - z * sin(x_angle) 
				//New_Z = y * sin(x_angle) + z * cos(x_angle)
				
				y=R*cos(xaxisA+xoffA); //second half -z *sin(x_angle) nulls to Zero
				z=R*sin(xaxisA+xoffA); //second half +z *cos(x_angle) nulls to Zero

			// rotate Y axis (draw from strips in increasing positive Latitude)		
				// New_X = z * sin(y_angle) + x * cos(y_angle)
				// New_Z = z * cos(y_angle) + x * sin(y_angle) 
				x=z*sin(yaxisA+yoffA); // second half +x * cos(y_angle) nulls to Zero
				z=z*cos(yaxisA+yoffA); // second half +x * sin(y_angle) nulls to Zero
		
			// while coordinate point may be -delta, we need to offset 
			glTexCoord2f((yaxisA+yoffA+deltaA)/PI2,(xaxisA+xoffA)/PI); 
			glVertex3f(x, y, z);
		}
		glEnd(); // Done Drawing The Quad
		
	}
}
#endif
#endif 

/**
 * Generate textures and other resources here.
 */
void demo_init()
{
#ifdef textures
	// int textures
	int x,y,i;
	
	// todo - alter texture generation method to use less variables, and bitwize ops.
	for (y=0,i=0;y<256;y++)
		for (x=0;x<256;x++)
		{
			if (((y<128)&&(x<128)) || ((y>128)&&(x>128)))
			{
				gTextureData[i]=255; //r
				i++;
				gTextureData[i]=255; //g
				i++;
				gTextureData[i]=255; //b
				i++;
			}
			else
			{
				gTextureData[i]=255; //r
				i++;
				gTextureData[i]=0;//g
				i++;
				gTextureData[i]=0;//b
				i++;
			}	
		}

	glGenTextures(1, &gTexture[0]); 
	
	glBindTexture(GL_TEXTURE_2D, gTexture[0]);
	glTexImage2D(GL_TEXTURE_2D, 
				0, 
				3, 
				256, 
				256, 
				0, 
				GL_RGB, 
				GL_UNSIGNED_BYTE, 
				&gTextureData[0]);
	
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	glEnable(GL_TEXTURE_2D);

	glMatrixMode(GL_TEXTURE);  
	glLoadIdentity();
	glScalef(10,10,10); // this is used to tile the texture

	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
#endif 

	// init sphere & renderings 
	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix
	gluPerspective(45.0f,(GLfloat)MAIN_X/(GLfloat)MAIN_Y,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();

#ifdef sphere 
	#ifdef quadric
		gObj = gluNewQuadric();
		gluQuadricTexture(gObj, TRUE);
		gluQuadricDrawStyle(gObj, GLU_FILL);
	#else
	#endif
	// normally you would delete the Quadric at the end of the program.  However! 
	// this is a 4kb - so we rely on the os to clean up
	//gluDeleteQuadric(gObj);
#endif 
}

/**
 * Drawing a bouncing Amiga ball
 * t  - The current playback time, in samples
 * dt - The time difference since the last frame, in samples
 */
int demo_render(DWORD t, DWORD dt)
{
	float fRot;

	if (t < 22050*15) 
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
		glLoadIdentity();

		#ifdef sphere 
			fRot=t/200;
			glTranslatef(0.0f,sin(fRot/100+1.5f),-3.7f);
			glRotatef(fRot,0.0f,1.0f,1.0f);

			#ifdef quadric 
				gluSphere(gObj, 0.5f, 20, 20);						// Draw the sphere with a radius of 0.5
			#else  (alternative routine)
				custom_sphere();
			#endif
				
		#endif 
	return 1;
	}
	else
	return 0;
}

