//	ogl3d.cpp
//

#include "stdafx.h"
#include "ogl3d.h"
#include "bmpreader.h"

#include <math.h>
#include <vector>

const int OGL3D_EARTH_LIST = 111;
const int OGL3D_EARTH_LINES = 222;
const int SOLAR_SYSTEM_LIST = 333;

namespace GEO {
	const double PI = 3.14159265359;
	const double TWOPI = 6.28318530718;
	const double DE2RA = 0.01745329252;
	const double RA2DE = 57.2957795129;
	const double FLATTENING = 1.0/298.26;
	const double PIOVER2 = 1.570796326795;
}

OGL3D::OGL3D(void)
{
	InitOGL3D();
}

OGL3D::~OGL3D(void)
{
}

void OGL3D::SetTexture(DIBSection& dib)
{
	if (dib.IsCreated())
	{
		GenTexture(dib);
	}
}

void OGL3D::GenTexture(DIBSection& dib)
{
	UINT32 width = dib.Width();
	UINT32 height = dib.Height();

	int w, h;

	UINT32 max_dimension = width > height ? width : height;

	if (width >= 512) w = 512;
	else if (width >= 256) w = 256;
	else if (width >= 128) w = 128;
	else if (width >= 64) w = 64;
	else if (width >= 32) w = 32;
	else w = 16;

	if (height >= 512) h = 512;
	else if (height >= 256) h = 256;
	else if (height >= 128) h = 128;
	else if (height >= 64) h = 64;
	else if (height >= 32) h = 32;
	else h = 16;

	dib.ResizeImage(m_texture_dib,w,h);

	if (m_texture_dib.IsCreated())
	{
		m_have_texture = 1;
	}
}

void OGL3D::RenderPrimaryImage(void)
{
	const int NumLatitudes = 36;
	const int NumLongitudes = 72;

	double start_lat, start_lon;
	double theta1, phi1, theta2, phi2, lat_incr, lon_incr;

	GLdouble u[3], v[3], w[3], n[3];
	GLdouble R;

	if (!m_model_built)
	{
		glNewList( OGL3D_EARTH_LIST, GL_COMPILE);

		glColor3ub(0,0,224);

		if (m_have_texture)
		{
			glTexImage2D(GL_TEXTURE_2D,0,3,m_texture_dib.Width(),m_texture_dib.Height(),
						0,GL_BGR_EXT,GL_UNSIGNED_BYTE,(GLvoid *)m_texture_dib.GetBits());

			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

			//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
			//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

			glEnable( GL_TEXTURE_2D );
		}
		else
		{
			glDisable( GL_TEXTURE_2D );
		}

		m_model_built = 1;

		if ( m_draw_mode == DrawLines )
		{
			glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
		}
		else
		{
			glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
		}

		glBegin( GL_TRIANGLES );

		start_lat = -90;
		start_lon = 0.0;
		R = 1.0;

		lat_incr = 180.0 / NumLatitudes;
		lon_incr = 360.0 / NumLongitudes;

		int row, col;

		for (col = 0; col < NumLongitudes; col++){
			phi1 = (start_lon + col * lon_incr) * GEO::DE2RA;
			phi2 = (start_lon + (col + 1) * lon_incr) * GEO::DE2RA;

			for (row = 0; row < NumLatitudes; row++){
				theta1 = (start_lat + row * lat_incr) * GEO::DE2RA;
				theta2 = (start_lat + (row + 1) * lat_incr) * GEO::DE2RA;

				u[0] = R * cos(phi1) * cos(theta1);//x
				u[1] = R * sin(theta1);//y
				u[2] = R * sin(phi1) * cos(theta1);//z
				
				v[0] = R * cos(phi1) * cos(theta2);//x
				v[1] = R * sin(theta2);//y
				v[2] = R * sin(phi1) * cos(theta2);//z

				w[0] = R * cos(phi2) * cos(theta2);//x
				w[1] = R * sin(theta2);//y
				w[2] = R * sin(phi2) * cos(theta2);//z

				NormalVector(u,v,w,n);
				glNormal3dv(n);
				glTexCoord2d((180.0 - phi1*GEO::RA2DE)/360.0,(theta1 + GEO::PIOVER2)*GEO::RA2DE/180.0);
				glVertex3dv(u);
				glTexCoord2d((180.0 - phi1*GEO::RA2DE)/360.0,(theta2 + GEO::PIOVER2)*GEO::RA2DE/180.0);
				glVertex3dv(v);
				glTexCoord2d((180.0 - phi2*GEO::RA2DE)/360.0,(theta2 + GEO::PIOVER2)*GEO::RA2DE/180.0);
				glVertex3dv(w);

				v[0] = R * cos(phi2) * cos(theta1);//x
				v[1] = R * sin(theta1);//y
				v[2] = R * sin(phi2) * cos(theta1);//z

				NormalVector(u,w,v,n);
				glNormal3dv(n);
				glTexCoord2d((180.0 - phi1*GEO::RA2DE)/360.0,(theta1 + GEO::PIOVER2)*GEO::RA2DE/180.0);
				glVertex3dv(u);
				glTexCoord2d((180.0 - phi2*GEO::RA2DE)/360.0,(theta2 + GEO::PIOVER2)*GEO::RA2DE/180.0);
				glVertex3dv(w);
				glTexCoord2d((180.0 - phi2*GEO::RA2DE)/360.0,(theta1 + GEO::PIOVER2)*GEO::RA2DE/180.0);
				glVertex3dv(v);
			}
		}

		glEnd();

		if (m_have_texture)
		{
			glDisable( GL_TEXTURE_2D );
		}

		GLView3D::RenderPrimaryImage();

		glEndList();
	}

	glCallList( OGL3D_EARTH_LIST );
}

void SolarSystem3D::RenderSun(void)
{
}

void SolarSystem3D::RenderMercury(void)
{
}

void SolarSystem3D::RenderVenus(void)
{
}

void SolarSystem3D::RenderEarth(void)
{
}

void SolarSystem3D::RenderMars(void)
{
}

void SolarSystem3D::RenderJupiter(void)
{
}

void SolarSystem3D::RenderSaturn(void)
{
}

void SolarSystem3D::RenderUranus(void)
{
}

void SolarSystem3D::RenderNeptune(void)
{
}

void SolarSystem3D::RenderPluto(void)
{
}

SolarSystem3D::SolarSystem3D(void)
{
	m_model_built = 0;
	m_draw_mode = OGL3D::DrawTriangles;

	DIBSection dib;
	ReadBMPFile("et.bmp",dib);
	GenTexture(dib,m_earth_texture);
	ReadBMPFile("mt.bmp",dib);
	GenTexture(dib,m_mars_texture);
	ReadBMPFile("jt.bmp",dib);
	GenTexture(dib,m_jupiter_texture);
	ReadBMPFile("st.bmp",dib);
	GenTexture(dib,m_saturn_texture);
}

SolarSystem3D::~SolarSystem3D(void)
{
}

void SolarSystem3D::RenderTexturedSphere(DIBSection& dib, double radius)
{

	glTexImage2D(GL_TEXTURE_2D,0,3,dib.Width(),dib.Height(),
				0,GL_BGR_EXT,GL_UNSIGNED_BYTE,(GLvoid *)dib.GetBits());

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

	glEnable( GL_TEXTURE_2D );

	glBegin( GL_TRIANGLES );

	double start_lat = -90;
	double start_lon = 0.0;
	double R = radius;

	const int NumLatitudes = 36;
	const int NumLongitudes = 72;

	double lat_incr = 180.0 / NumLatitudes;
	double lon_incr = 360.0 / NumLongitudes;

	double phi1, phi2, theta1, theta2;
	GLdouble u[3], v[3], w[3], n[3];

	int row, col;

	for (col = 0; col < NumLongitudes; col++){
		phi1 = (start_lon + col * lon_incr) * GEO::DE2RA;
		phi2 = (start_lon + (col + 1) * lon_incr) * GEO::DE2RA;

		for (row = 0; row < NumLatitudes; row++){
			theta1 = (start_lat + row * lat_incr) * GEO::DE2RA;
			theta2 = (start_lat + (row + 1) * lat_incr) * GEO::DE2RA;

			u[0] = R * cos(phi1) * cos(theta1);//x
			u[1] = R * sin(theta1);//y
			u[2] = R * sin(phi1) * cos(theta1);//z
			
			v[0] = R * cos(phi1) * cos(theta2);//x
			v[1] = R * sin(theta2);//y
			v[2] = R * sin(phi1) * cos(theta2);//z

			w[0] = R * cos(phi2) * cos(theta2);//x
			w[1] = R * sin(theta2);//y
			w[2] = R * sin(phi2) * cos(theta2);//z

			NormalVector(u,v,w,n);
			glNormal3dv(n);
			glTexCoord2d((180.0 - phi1*GEO::RA2DE)/360.0,(theta1 + GEO::PIOVER2)*GEO::RA2DE/180.0);
			glVertex3dv(u);
			glTexCoord2d((180.0 - phi1*GEO::RA2DE)/360.0,(theta2 + GEO::PIOVER2)*GEO::RA2DE/180.0);
			glVertex3dv(v);
			glTexCoord2d((180.0 - phi2*GEO::RA2DE)/360.0,(theta2 + GEO::PIOVER2)*GEO::RA2DE/180.0);
			glVertex3dv(w);

			v[0] = R * cos(phi2) * cos(theta1);//x
			v[1] = R * sin(theta1);//y
			v[2] = R * sin(phi2) * cos(theta1);//z

			NormalVector(u,w,v,n);
			glNormal3dv(n);
			glTexCoord2d((180.0 - phi1*GEO::RA2DE)/360.0,(theta1 + GEO::PIOVER2)*GEO::RA2DE/180.0);
			glVertex3dv(u);
			glTexCoord2d((180.0 - phi2*GEO::RA2DE)/360.0,(theta2 + GEO::PIOVER2)*GEO::RA2DE/180.0);
			glVertex3dv(w);
			glTexCoord2d((180.0 - phi2*GEO::RA2DE)/360.0,(theta1 + GEO::PIOVER2)*GEO::RA2DE/180.0);
			glVertex3dv(v);
		}
	}

	glDisable( GL_TEXTURE_2D );

	glEnd();
}

void SolarSystem3D::RenderColoredSphere(COLORREF cr, double radius)
{
	glDisable( GL_TEXTURE_2D );

	glBegin( GL_TRIANGLES );

	glColor3ub(GetRValue(cr),GetGValue(cr),GetBValue(cr));

	double start_lat = -90;
	double start_lon = 0.0;
	double R = radius;

	const int NumLatitudes = 18;
	const int NumLongitudes = 36;

	double lat_incr = 180.0 / NumLatitudes;
	double lon_incr = 360.0 / NumLongitudes;

	double phi1, phi2, theta1, theta2;
	GLdouble u[3], v[3], w[3], n[3];

	int row, col;

	for (col = 0; col < NumLongitudes; col++){
		phi1 = (start_lon + col * lon_incr) * GEO::DE2RA;
		phi2 = (start_lon + (col + 1) * lon_incr) * GEO::DE2RA;

		for (row = 0; row < NumLatitudes; row++){
			theta1 = (start_lat + row * lat_incr) * GEO::DE2RA;
			theta2 = (start_lat + (row + 1) * lat_incr) * GEO::DE2RA;

			u[0] = R * cos(phi1) * cos(theta1);//x
			u[1] = R * sin(theta1);//y
			u[2] = R * sin(phi1) * cos(theta1);//z
			
			v[0] = R * cos(phi1) * cos(theta2);//x
			v[1] = R * sin(theta2);//y
			v[2] = R * sin(phi1) * cos(theta2);//z

			w[0] = R * cos(phi2) * cos(theta2);//x
			w[1] = R * sin(theta2);//y
			w[2] = R * sin(phi2) * cos(theta2);//z

			NormalVector(u,v,w,n);
			glNormal3dv(n);
			glVertex3dv(u);
			glVertex3dv(v);
			glVertex3dv(w);

			v[0] = R * cos(phi2) * cos(theta1);//x
			v[1] = R * sin(theta1);//y
			v[2] = R * sin(phi2) * cos(theta1);//z

			NormalVector(u,w,v,n);
			glNormal3dv(n);
			glVertex3dv(u);
			glVertex3dv(w);
			glVertex3dv(v);
		}
	}

	glEnd();
}

void SolarSystem3D::GenTexture(DIBSection& dib, DIBSection& texture_dib)
{
	UINT32 width = dib.Width();
	UINT32 height = dib.Height();

	int w, h;

	UINT32 max_dimension = width > height ? width : height;

	if (width >= 512) w = 512;
	else if (width >= 256) w = 256;
	else if (width >= 128) w = 128;
	else if (width >= 64) w = 64;
	else if (width >= 32) w = 32;
	else w = 16;

	if (height >= 512) h = 512;
	else if (height >= 256) h = 256;
	else if (height >= 128) h = 128;
	else if (height >= 64) h = 64;
	else if (height >= 32) h = 32;
	else h = 16;

	dib.ResizeImage(texture_dib,w,h);
}

int SolarSystem3D::PickPlanet(int x, int y, CString& msg)
{
	int result = -1;

	if ( m_draw_mode == OGL3D::DrawLines )
	{
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
	}
	else
	{
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
	}

	glShadeModel(GL_FLAT);
	glDisable(GL_LIGHT0);
	glDisable(GL_LIGHTING);

	glClearColor((float)1.00,(float)1.00,
		(float)1.00,1.0f);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
	glTranslated(m_model_matrix.X(),m_model_matrix.Y(),m_model_matrix.Z());
	glRotated(m_model_matrix.XRot(), 1.0, 0.0, 0.0);
	glRotated(m_model_matrix.YRot(), 0.0, 1.0, 0.0);
	glRotated(m_model_matrix.ZRot(), 0.0, 0.0, 1.0);
	glScaled(m_model_matrix.XScale(),m_model_matrix.YScale(),m_model_matrix.ZScale());

	glScaled(0.40,0.40,0.40);

	COLORREF cr;
	std::vector<COLORREF> colorVector;

	cr = 1;
	RenderColoredSphere(cr,0.55);
	colorVector.push_back(cr);

	glPushMatrix();
	glRotated(90.0, 0.0, 1.0, 0.0);
	glTranslated(1.0,0.0,0.0);
	cr = 2;
	RenderColoredSphere(cr,0.10);
	colorVector.push_back(cr);
	glPopMatrix();

	glPushMatrix();
	glRotated(180.0, 0.0, 1.0, 0.0);
	glTranslated(1.5,0.0,0.0);
	cr = 3;
	RenderColoredSphere(cr,0.15);
	colorVector.push_back(cr);
	glPopMatrix();

	glPushMatrix();
	glRotated(270.0, 0.0, 1.0, 0.0);
	glTranslated(2.0,0.0,0.0);
	cr = 4;
	RenderColoredSphere(cr,0.20);
	colorVector.push_back(cr);
	glPopMatrix();

	glPushMatrix();
	glRotated(0.0, 0.0, 1.0, 0.0);
	glTranslated(2.5,0.0,0.0);
	cr = 5;
	RenderColoredSphere(cr,0.15);
	colorVector.push_back(cr);
	glPopMatrix();

	glPushMatrix();
	glRotated(45.0, 0.0, 1.0, 0.0);
	glTranslated(3.5,0.0,0.0);
	cr = 6;
	RenderColoredSphere(cr,0.40);
	colorVector.push_back(cr);
	glPopMatrix();

	glPushMatrix();
	glRotated(135.0, 0.0, 1.0, 0.0);
	glTranslated(4.5,0.0,0.0);
	cr = 7;
	RenderColoredSphere(cr,0.25);
	colorVector.push_back(cr);
	glPopMatrix();

	glPushMatrix();
	glRotated(225.0, 0.0, 1.0, 0.0);
	glTranslated(5.0,0.0,0.0);
	cr = 8;
	RenderColoredSphere(cr,0.15);
	colorVector.push_back(cr);
	glPopMatrix();

	glPushMatrix();
	glRotated(315.0, 0.0, 1.0, 0.0);
	glTranslated(5.5,0.0,0.0);
	cr = 9;
	RenderColoredSphere(cr,0.15);
	colorVector.push_back(cr);
	glPopMatrix();

	glPushMatrix();
	glRotated(15.0, 0.0, 1.0, 0.0);
	glTranslated(6.0,0.0,0.0);
	cr = 10;
	RenderColoredSphere(cr,0.15);
	colorVector.push_back(cr);
	glPopMatrix();

	glPopMatrix();

	cr = 0;
	m_dib.GetPixel(x,y,cr);
	
	msg = "missed";

	char * planets[10] = {"Sun","Mercury","Venus","Earth","Mars",
		"Jupiter","Saturn","Uranus","Neptune","Pluto"};

	int color_size = colorVector.size();
	for (int i=0;i<color_size;i++){
		if (cr == colorVector.at(i))
		{
			msg = planets[i];
			break;
		}
	}

	RenderImage();

	return result;
}

void SolarSystem3D::RenderPrimaryImage(void)
{
	if (!m_model_built)
	{
		glNewList( SOLAR_SYSTEM_LIST, GL_COMPILE);

		if ( m_draw_mode == OGL3D::DrawLines )
		{
			glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
		}
		else
		{
			glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
		}

		glPushMatrix();
		glScaled(0.40,0.40,0.40);

		RenderColoredSphere(RGB(255,255,0),0.55);

		glPushMatrix();
		glRotated(90.0, 0.0, 1.0, 0.0);
		glTranslated(1.0,0.0,0.0);
		RenderColoredSphere(RGB(128,0,0),0.10);
		glPopMatrix();

		glPushMatrix();
		glRotated(180.0, 0.0, 1.0, 0.0);
		glTranslated(1.5,0.0,0.0);
		RenderColoredSphere(RGB(255,128,0),0.15);
		glPopMatrix();

		glPushMatrix();
		glRotated(270.0, 0.0, 1.0, 0.0);
		glTranslated(2.0,0.0,0.0);
		RenderTexturedSphere(m_earth_texture,0.20);
		glPopMatrix();

		glPushMatrix();
		glRotated(0.0, 0.0, 1.0, 0.0);
		glTranslated(2.5,0.0,0.0);
		RenderTexturedSphere(m_mars_texture,0.15);
		glPopMatrix();

		glPushMatrix();
		glRotated(45.0, 0.0, 1.0, 0.0);
		glTranslated(3.5,0.0,0.0);
		RenderTexturedSphere(m_jupiter_texture,0.40);
		glPopMatrix();

		glPushMatrix();
		glRotated(135.0, 0.0, 1.0, 0.0);
		glTranslated(4.5,0.0,0.0);
		RenderTexturedSphere(m_saturn_texture,0.25);
		glPopMatrix();

		glPushMatrix();
		glRotated(225.0, 0.0, 1.0, 0.0);
		glTranslated(5.0,0.0,0.0);
		RenderColoredSphere(RGB(0,0,128),0.15);
		glPopMatrix();

		glPushMatrix();
		glRotated(315.0, 0.0, 1.0, 0.0);
		glTranslated(5.5,0.0,0.0);
		RenderColoredSphere(RGB(0,128,128),0.15);
		glPopMatrix();

		glPushMatrix();
		glRotated(15.0, 0.0, 1.0, 0.0);
		glTranslated(6.0,0.0,0.0);
		RenderColoredSphere(RGB(224,224,224),0.15);
		glPopMatrix();

		glPopMatrix();

		glEndList();
	}

	glCallList( SOLAR_SYSTEM_LIST );

}