home dev games gallery work
Lewpen.com»Research & Development»3D Graphics»OpenGL»Reflective Sphere

Reflective Sphere

Analytical reflections in OpenGL

/ Source / test.cpp

#define WIN32_LEAN_AND_MEAN


/*
*/


#include <math.h>

#include	"qe_ogl.h"
#include "mmsystem.h"

double r1=0, r2=0;
BOOL DrawMe = TRUE;

#define LENGTH 400

double ax[1000], ay[1000], az[1000];
double ax1[1000], ay1[1000], az1[1000];
double ax2[1000], ay2[1000], az2[1000];



#define DEFAULT_SUBDIVIDE_LEVEL 2
#define DEFAULT_REFLECTION_ITERATIONS 10




//---- INCLUDE COMMON FILES




GLuint	texture[1];


template <class T>
class Pair
{
private:
	T p[2];
public:
	Pair()
	{
	}

	Pair( T a, T b )
	{
		p[0] = a;
		p[1] = b;
	}

	T a() { return p[0]; }
	T b() { return p[1]; }
};



class Colour
{
private:
	double c[4];

public:
	Colour( double r, double g, double b, double a = 1.0 )
	{
		c[0] = r;
		c[1] = g;
		c[2] = b;
		c[3] = a;
	}

	double r() { return c[0]; }
	double g() { return c[1]; }
	double b() { return c[2]; }
	double a() { return c[3]; }

	Colour darker( double v = 0.8 )
	{
		return Colour( c[0]*v, c[1]*v, c[2]*v, c[3] );
	}

	Colour lighter( double v = 0.8 )
	{
		return Colour( 1.0*(1.0-v)+c[0]*v, 1.0*(1.0-v)+c[1]*v, 1.0*(1.0-v)+c[2]*v, c[3] );
	}

	Colour interpolate( Colour col, double v = 0.5 )
	{
		return Colour
		(
			c[0]*v+col.c[0]*(1.0-v),
			c[1]*v+col.c[1]*(1.0-v),
			c[2]*v+col.c[2]*(1.0-v),
			c[3]*v+col.c[3]*(1.0-v)
		);
	}

};



class vec3
{
private:
	double p[3];

public:
	vec3()
	{
		p[0] = p[1] = p[2] = 0;
	}

	vec3( double x, double y, double z )
	{
		p[0] = x;
		p[1] = y;
		p[2] = z;
	}

	double x() { return p[0]; }
	double y() { return p[1]; }
	double z() { return p[2]; }

	double length2()
	{
		return p[0]*p[0] + p[1]*p[1] + p[2]*p[2];
	}

	double length()
	{
		return sqrt( length2() );
	}

	double dot( vec3 a )
	{
		return p[0]*a.p[0] + p[1]*a.p[1] + p[2]*a.p[2];
	}

	vec3 cross( vec3 a )
	{
		return vec3
		(
			p[1]*a.p[2] - p[2]*a.p[1],
			p[2]*a.p[0] - p[0]*a.p[2],
			p[0]*a.p[1] - p[1]*a.p[0]
		);
	}

	vec3 operator * ( vec3 a )
	{
		return cross( a );
	}

	vec3 add( vec3 a )
	{
		return vec3
		(
			p[0] + a.p[0],
			p[1] + a.p[1],
			p[2] + a.p[2]
		);
	}

	vec3 operator + ( vec3 a )
	{
		return add( a );
	}

	vec3 sub( vec3 a )
	{
		return vec3
		(
			p[0] - a.p[0],
			p[1] - a.p[1],
			p[2] - a.p[2]
		);
	}

	vec3 operator - ( vec3 a )
	{
		return sub( a );
	}

	vec3 scale( double d )
	{
		return vec3( p[0]*d, p[1]*d, p[2]*d );
	}

	vec3 operator * ( double d )
	{
		return scale( d );
	}

	vec3 operator / ( double d )
	{
		return scale( 1.0 / d );
	}

	vec3 normalise()
	{
		double l = length();
		return l > 0 ? scale( 1.0 / l ) : vec3();
	}

	vec3 interpolate( vec3 b, double d )
	{
		return vec3
		(
			p[0] * (1.0-d) + b.p[0] * d,
			p[1] * (1.0-d) + b.p[1] * d,
			p[2] * (1.0-d) + b.p[2] * d
		);
	}

	vec3 rotate( int axis1, int axis2, double r )
	{
		vec3 a = *this;

		if( axis1 >= 0 && axis1 < 3 && axis2 >= 0 && axis2 < 3 )
		{
			a.p[ axis1 ] = p[ axis1 ] * sin(r) + p[ axis2 ] * cos(r);
			a.p[ axis2 ] = p[ axis1 ] * cos(r) - p[ axis2 ] * sin(r);
		}

		return a;
	}

};



class vec4
{
private:
	double p[4];

public:

	vec4()
	{
		p[0] = p[1] = p[2] = p[3] = 0;
	}

	vec4( double x, double y, double z, double w )
	{
		p[0] = x;
		p[1] = y;
		p[2] = z;
		p[3] = w;
	}

	double x() { return p[0]; }
	double y() { return p[1]; }
	double z() { return p[2]; }
	double w() { return p[3]; }

	double length2()
	{
		return p[0]*p[0] + p[1]*p[1] + p[2]*p[2] + p[3]*p[3];
	}

	double length()
	{
		return sqrt( length2() );
	}

	double dot( vec4 a )
	{
		return p[0]*a.p[0] + p[1]*a.p[1] + p[2]*a.p[2] + p[3]*a.p[3];
	}

	vec4 add( vec4 a )
	{
		return vec4
		(
			p[0]+a.p[0],
			p[1]+a.p[1],
			p[2]+a.p[2],
			p[3]+a.p[3]
		);
	}

	vec4 operator + ( vec4 a )
	{
		return add( a );
	}

	vec4 sub( vec4 a )
	{
		return vec4
		(
			p[0] - a.p[0],
			p[1] - a.p[1],
			p[2] - a.p[2],
			p[3] - a.p[3]
		);
	}

	vec4 operator - ( vec4 a )
	{
		return sub( a );
	}

	vec4 scale( double d )
	{
		return vec4( p[0]*d, p[1]*d, p[2]*d, p[3]*d );
	}

	vec4 operator * ( double d )
	{
		return scale( d );
	}

	vec4 operator / ( double d )
	{
		return scale( 1.0 / d );
	}

	vec4 normalise()
	{
		double l = length();
		return l > 0 ? scale( 1.0 / l ) : vec4();
	}

	vec4 interpolate( vec4 b, double d )
	{
		return vec4
		(
			p[0] * (1.0-d) + b.p[0] * d,
			p[1] * (1.0-d) + b.p[1] * d,
			p[2] * (1.0-d) + b.p[2] * d,
			p[3] * (1.0-d) + b.p[3] * d
		);
	}

	vec3 stereographicProjection()
	{
		double oo1mz = 1.0 / ( 1.0 - p[3] );

		return vec3
		(
			p[0] * oo1mz,
			p[1] * oo1mz,
			p[2] * oo1mz
		);
	}

	vec4 rotate( int axis1, int axis2, double r )
	{
		vec4 a = *this;

		if( axis1 >= 0 && axis1 < 4 && axis2 >= 0 && axis2 < 4 )
		{
			a.p[ axis1 ] = p[ axis1 ] * sin(r) + p[ axis2 ] * cos(r);
			a.p[ axis2 ] = p[ axis1 ] * cos(r) - p[ axis2 ] * sin(r);
		}

		return a;
	}

	vec4 rotate6( double r[6] )
	{
		return
			rotate( 0, 1, r[0] ).
			rotate( 0, 2, r[1] ).
			rotate( 0, 3, r[2] ).
			rotate( 1, 2, r[3] ).
			rotate( 1, 3, r[4] ).
			rotate( 2, 3, r[5] );
	}
};







vec3 viewPoint;

vec3 sphereOrigin( 0, 4, 0 );
double sphereRadius = 2.0;







class Plane3
{
private:
	vec3 point, normal;

public:

	Plane3( double dist, vec3 normal )
		: point( normal*dist ), normal( normal )
	{}

	Plane3( vec3 point, vec3 normal )
		: point( point ), normal( normal )
	{}

	Plane3( vec3 p1, vec3 p2, vec3 p3 )
		: point( p1 ), normal( p2 * p3 )
	{}

};



double sphere_x[16][17];
double sphere_y[16][17];
double sphere_z[16][17];
bool sphereDone = false;

void CreateSphere()
{
  int i, j;
  double x, y, z;

  if( !sphereDone)
  {
	  double sr = 1;
  for(j=0; j<16; j++) for(i=0; i<=16; i++) {
    x = sr*sin(i*3.14159/16);
    y = sr*cos(i*3.14159/16);
    z = 0;

    //  Rotate about y axis

    double nx = z*sin(j*3.14159/8) + x*cos(j*3.14159/8);
    double ny = y;
    double nz = z*cos(j*3.14159/8) - x*sin(j*3.14159/8);

    sphere_x[j][i] = nx;
    sphere_y[j][i] = ny;
    sphere_z[j][i] = nz;
  }
  sphereDone = true;
  }
}

void Sphere(double sx, double sy, double sz, double sr, bool checks = true )
{
	CreateSphere();

int i, j;
  double r, g, b;
  int ii, jj;

  for(j=0; j<16; j++) for(i=0; i<16; i++) {

    ii = (i+1);
    jj = (j+1)&15;

	if( checks )
	{
		if(((i^j)&1) == 0) {
		r = 1.0; g = 1.0; b = 1.0;
		glColor4f(r, g, b, 0.3 );
		} else {
		r = 1.0; g = 0.0; b = 0.0;
		glColor4f(r, g, b, 0.3 );
		}
	}

    glBegin(GL_TRIANGLE_STRIP);
      glVertex3f(sx+sphere_x[jj][ii]*sr, sy+sphere_y[jj][ii]*sr, sz+sphere_z[jj][ii]*sr);
      glVertex3f(sx+sphere_x[ j][ii]*sr, sy+sphere_y[ j][ii]*sr, sz+sphere_z[ j][ii]*sr);
      glVertex3f(sx+sphere_x[jj][ i]*sr, sy+sphere_y[jj][ i]*sr, sz+sphere_z[jj][ i]*sr);
      glVertex3f(sx+sphere_x[ j][ i]*sr, sy+sphere_y[ j][ i]*sr, sz+sphere_z[ j][ i]*sr);
    glEnd();

  }
   
}

void Sphere( vec3 c, double r )
{
	Sphere( c.x(), c.y(), c.z(), r );
}



vec3 SpherePoint( vec3 c, double r, int i, int j )
{
	return vec3( c.x()+r*sphere_x[j][i], c.y()+r*sphere_y[j][i], c.z()+r*sphere_z[j][i] );
}

vec3 SphereNormal( int i, int j )
{
	return vec3( sphere_x[j][i], sphere_y[j][i], sphere_z[j][i] ) * -1;
}

bool ReflectFloor( vec3 c, double r, vec3 n, vec3 &q, vec3 &rq )
{
	vec3 p = c + n * r;

	vec3 a = (p-viewPoint).normalise();

	vec3 rv = (a - n*a.dot(n)*2.0 );

	if( rv.y() >= 0 ) return false;

	double t = -p.y() / rv.y();

	q = p + rv * t;

	rq = p + a * (q-p).length();

	return true;
}

void glVertex( vec3 p )
{
	glVertex3f( p.x(), p.y(), p.z() );
}

void SphereQuadSubdivide( vec3 c, double r, vec3 n1, vec3 n2, vec3 n3, vec3 n4, int level=2 )
{
	vec3 q1, q2, q3, q4;
	vec3 r1, r2, r3, r4;

	bool ok1 = ReflectFloor( c, r, n1, q1, r1 );
	bool ok2 = ReflectFloor( c, r, n2, q2, r2 );
	bool ok3 = ReflectFloor( c, r, n3, q3, r3 );
	bool ok4 = ReflectFloor( c, r, n4, q4, r4 );

	if( ok1 && ok2 && ok3 && ok4 && level == 0 )
	{
		glBegin(GL_QUADS);
			glTexCoord2f( q1.x()*0.1, q1.z()*0.1 ); glVertex( r1 );
			glTexCoord2f( q2.x()*0.1, q2.z()*0.1 ); glVertex( r2 );
			glTexCoord2f( q3.x()*0.1, q3.z()*0.1 ); glVertex( r3 );
			glTexCoord2f( q4.x()*0.1, q4.z()*0.1 ); glVertex( r4 );
		glEnd();
	}
	else if( (ok1 || ok2 || ok3 || ok4) && level > 0 )
	{
		vec3 n12 = ((n1+n2)*0.5).normalise();
		vec3 n23 = ((n2+n3)*0.5).normalise();
		vec3 n34 = ((n3+n4)*0.5).normalise();
		vec3 n41 = ((n4+n1)*0.5).normalise();
		vec3 nm  = ((n1+n3)*0.5).normalise();
		
		SphereQuadSubdivide( c, r, n1, n12, nm, n41, level-1 );
		SphereQuadSubdivide( c, r, n12, n2, n23, nm, level-1 );
		SphereQuadSubdivide( c, r, nm, n23, n3, n34, level-1 );
		SphereQuadSubdivide( c, r, n41, nm, n34, n4, level-1 );
	}
}

void SphereFloorReflection( vec3 c, double r )
{
	glEnable( GL_TEXTURE_2D );
	glBindTexture( GL_TEXTURE_2D, texture[0] );

    glColor4f( 1, 1, 1, 1 );

	for( int j=0; j<16; j++ ) for( int i=0; i<16; i++ )
	{
		int ii = (i+1);
		int jj = (j+1)&15;

		vec3 n1 = SphereNormal(  i,  j );
		vec3 n2 = SphereNormal( ii,  j );
		vec3 n3 = SphereNormal( ii, jj );
		vec3 n4 = SphereNormal(  i, jj );

		SphereQuadSubdivide( c, r, n1, n2, n3, n4 );		
	}

	glDisable( GL_TEXTURE_2D );
}


//---- getViewPoint

vec3 getViewPoint()
{
	double vx = 0, vy = 0, vz = 0;

	double wx, wy, wz;

//  r1 = -r1;
//  r2 = -r2;

    vx = 0;
    vy = 0;
    vz = 15;

    wx = vx;
    wy = vy*cos(0.2*r2*3.14159/180)-vz*sin(0.2*r2*3.14159/180);
    wz = -(vy*sin(0.2*r2*3.14159/180)+vz*cos(0.2*r2*3.14159/180));

    vx = wx*cos(0.2*r1*3.14159/180)-wz*sin(0.2*r1*3.14159/180);
    vy = wy;
    vz = -(wx*sin(0.2*r1*3.14159/180)+wz*cos(0.2*r1*3.14159/180));

	return vec3( vx, vy+4.0, vz );
}



void getscreenpoint(double *mx, double *my, double *mz, double w1, double w2, double w3, double r1, double r2)
{
  double vx = 0, vy = 0, vz = 0;

  double wx, wy, wz;

  r1 = -r1;
  r2 = -r2;

  vx = wx*cos(0.2*r1*3.14159/180)-wz*sin(0.2*r1*3.14159/180);
  vy = wy;
  vz = -(wx*sin(0.2*r1*3.14159/180)+wz*cos(0.2*r1*3.14159/180));

  wx = vx;
  wy = vy*cos(0.2*r2*3.14159/180)-vz*sin(0.2*r2*3.14159/180);
  wz = -(vy*sin(0.2*r2*3.14159/180)+vz*cos(0.2*r2*3.14159/180));

  *mx = wx; *my = wy; *mz = wz;
}



//---- NEAREST POINT

//  Given a line starting at a and going to b, finds the nearest the line goes to point p

double getPointNearestDistance( vec3 a, vec3 b, vec3 p )
{
	return ( (b-a) * (a-p) ).length() / (b-a).length();
}

vec3 getNearestPointOnLine( vec3 a, vec3 b, vec3 p )
{
	vec3 d = b-a;

	double t = d.dot( p-a ) / d.length2();

	return a + d * t;
}



//---- REFLECTION

//  o = view point
//  c = centre of sphere
//  r = radius of sphere
//  p = position of point to reflect


//  Returns true if there is a reflected point

bool reflectedPointExists( vec3 o, vec3 c, double r, vec3 p )
{
	return (p-c).length2() >= r*r && ( getPointNearestDistance( o, p, c ) >= r || (p-o).length2() < (c-o).length2() );
}

//  Returns the reflected point

vec3 getReflectedPointOnSphere( vec3 o, vec3 c, double r, vec3 p )
{
	//  s is the interpolated point

	vec3 s0 = o;
	vec3 s1 = getNearestPointOnLine( o, p, c );

	int count = DEFAULT_REFLECTION_ITERATIONS;

	while( true )
	{
		//  Test point

		vec3 s = (s0+s1) * 0.5;

		//  Direction from centre of sphere

		vec3 n = (s-c).normalise();

		//  Map to surface of sphere

		vec3 q = c + n * r;

		//  Get reflected p

		vec3 a = q-o;
		vec3 rv = (a - n*a.dot(n)*2.0 ).normalise();  //  reflection vector from q
		vec3 pv = (p-q).normalise();                  //  vector from q to p

		double arv = a.dot( rv );
		double apv = a.dot( pv );

		count --;

		if( arv == apv || count <= 0 )
		{
			return q;
		}
		else if( arv > apv )
		{
			s1 = s;
		}
		else
		{
			s0 = s;
		}
	}
}



vec3 getReflectedPoint( vec3 o, vec3 c, double r, vec3 p )
{
	vec3 q = getReflectedPointOnSphere( o, c, r, p );
	vec3 n = (q-c).normalise();

	vec3 a = (q-o).normalise();

	return q + a * (q-p).length();
}



//---- Triangle

void Triangle( vec3 a, vec3 b, vec3 c )
{
  glBegin(GL_TRIANGLE_FAN);
    glVertex3f( a.x(), a.y(), a.z() );
    glVertex3f( b.x(), b.y(), b.z() );
    glVertex3f( c.x(), c.y(), c.z() );
  glEnd();
}








void ReflectedTriangle( vec3 a, vec3 b, vec3 c )
{
	if( ! reflectedPointExists( viewPoint, sphereOrigin, sphereRadius, a ) ) return;
	if( ! reflectedPointExists( viewPoint, sphereOrigin, sphereRadius, b ) ) return;
	if( ! reflectedPointExists( viewPoint, sphereOrigin, sphereRadius, c ) ) return;

	Triangle
	(
		getReflectedPoint( viewPoint, sphereOrigin, sphereRadius, a ),
		getReflectedPoint( viewPoint, sphereOrigin, sphereRadius, b ),
		getReflectedPoint( viewPoint, sphereOrigin, sphereRadius, c )
	);
}



//---- ReflectedQuad

void ReflectedQuad( vec3 a, vec3 b, vec3 c, vec3 d )
{
	ReflectedTriangle( a, b, c );
	ReflectedTriangle( c, d, a );
}



//---- SubdividedReflectedQuad

void SubdividedReflectedQuad_Recurse( vec3 a, vec3 b, vec3 c, vec3 d, int level = DEFAULT_SUBDIVIDE_LEVEL )
{
	if( level > 0 )
	{
		vec3 ab = (a+b) * 0.5;
		vec3 bc = (b+c) * 0.5;
		vec3 cd = (c+d) * 0.5;
		vec3 da = (d+a) * 0.5;
		vec3 m  = (a+c) * 0.5;

		SubdividedReflectedQuad_Recurse( a, ab, m, da, level-1 );
		SubdividedReflectedQuad_Recurse( ab, b, bc, m, level-1 );
		SubdividedReflectedQuad_Recurse( m, bc, c, cd, level-1 );
		SubdividedReflectedQuad_Recurse( da, m, cd, d, level-1 );
	}
	else
	{
		ReflectedQuad( a, b, c, d );
	}
}

void SubdividedReflectedQuad( vec3 a, vec3 b, vec3 c, vec3 d, Colour col, int level = DEFAULT_SUBDIVIDE_LEVEL )
{
	glColor3f( col.r(), col.g(), col.b() );
	SubdividedReflectedQuad_Recurse( a, b, c, d, level );
}


//---- ReflectedCube

void ReflectedCube( vec3 a, vec3 b, Colour col )
{
	double x1 = b.x();
	double y1 = b.y();
	double z1 = b.z();

	double x2 = a.x();
	double y2 = a.y();
	double z2 = a.z();

	SubdividedReflectedQuad
	(
		vec3(x1, y1, z1),
		vec3(x1, y1, z2),
		vec3(x2, y1, z2),
		vec3(x2, y1, z1),
		col.lighter()
	);

	SubdividedReflectedQuad
	(
		vec3(x2, y2, z1),
		vec3(x2, y2, z2),
		vec3(x1, y2, z2),
		vec3(x1, y2, z1),
		col.lighter()
	);

	SubdividedReflectedQuad
	(
		vec3(x1, y2, z1),
		vec3(x1, y2, z2),
		vec3(x1, y1, z2),
		vec3(x1, y1, z1),
		col
	);

	SubdividedReflectedQuad
	(
		vec3(x2, y1, z1),
		vec3(x2, y1, z2),
		vec3(x2, y2, z2),
		vec3(x2, y2, z1),
		col
	);

	SubdividedReflectedQuad
	(
		vec3(x2, y1, z1),
		vec3(x2, y2, z1),
		vec3(x1, y2, z1),
		vec3(x1, y1, z1),
		col.darker()
	);

	SubdividedReflectedQuad
	(
		vec3(x1, y1, z2),
		vec3(x1, y2, z2),
		vec3(x2, y2, z2),
		vec3(x2, y1, z2),
		col.darker()
	);
}

void ReflectedCube( vec3 c, double r, Colour col )
{
	ReflectedCube( c - vec3(r,r,r), c + vec3(r,r,r), col );
}




void ReflectedSphere(double sx, double sy, double sz, double sr)
{
	CreateSphere();

int i, j;
  double r, g, b;
  int ii, jj;

  for(j=0; j<16; j++) for(i=0; i<16; i++) {

    ii = (i+1);
    jj = (j+1)&15;

    if(((i^j)&1) == 0) {
      r = 1.0; g = 1.0; b = 1.0;
    } else {
      r = 1.0; g = 0.0; b = 0.0;
    }

      glColor4f(r, g, b, 1.0 );
    ReflectedQuad
	(
      vec3(sx+sphere_x[ j][ii]*sr, sy+sphere_y[ j][ii]*sr, sz+sphere_z[ j][ii]*sr),
      vec3(sx+sphere_x[jj][ii]*sr, sy+sphere_y[jj][ii]*sr, sz+sphere_z[jj][ii]*sr),
      vec3(sx+sphere_x[jj][ i]*sr, sy+sphere_y[jj][ i]*sr, sz+sphere_z[jj][ i]*sr),
      vec3(sx+sphere_x[ j][ i]*sr, sy+sphere_y[ j][ i]*sr, sz+sphere_z[ j][ i]*sr)
    );

  }
}



//---- Cube

void Cube(GLfloat x1, GLfloat y1, GLfloat z1, GLfloat x2, GLfloat y2, GLfloat z2, GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
  glColor4f(r/0.8, g/0.8, b/0.8,a);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y1, z1);
    glVertex3f(x1, y1, z2);
    glVertex3f(x2, y1, z2);
    glVertex3f(x2, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y2, z1);
    glVertex3f(x2, y2, z2);
    glVertex3f(x1, y2, z2);
    glVertex3f(x1, y2, z1);
  glEnd();

  glColor4f(r, g, b,a);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y2, z1);
    glVertex3f(x1, y2, z2);
    glVertex3f(x1, y1, z2);
    glVertex3f(x1, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y1, z1);
    glVertex3f(x2, y1, z2);
    glVertex3f(x2, y2, z2);
    glVertex3f(x2, y2, z1);
  glEnd();

  glColor4f(r*0.80, g*0.80, b*0.80,a);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y1, z1);
    glVertex3f(x2, y2, z1);
    glVertex3f(x1, y2, z1);
    glVertex3f(x1, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y1, z2);
    glVertex3f(x1, y2, z2);
    glVertex3f(x2, y2, z2);
    glVertex3f(x2, y1, z2);
  glEnd();

}


void Cube( vec3 pos, double size, Colour col )
{
	Cube
	(
		pos.x()-size, pos.y()-size, pos.z()-size,
		pos.x()+size, pos.y()+size, pos.z()+size,
		col.r(), col.g(), col.b(), col.a()
	);
}







double getStereographicVolume( vec3 a )
{
	double d = 1.0 + a.length2();
	return ( d*d ) / 8.0;
}



double cubeRoot( double a )
{
	return exp( log(a) / 3.0 );
}



void drawSchlegalThickLine( vec4 a, vec4 b )
{
	for( int i=0; i<=100; i++ )
	{
		vec3 p = a.interpolate( b, i*0.01 ).normalise().stereographicProjection();
		double v = getStereographicVolume( p );
		double l = cubeRoot( v ) * 0.03;
		
		Cube( p, l, Colour( 0.3, 0.6, 0.9 ) );
	}
}

void drawSchlegalPoint( vec4 a )
{
	vec3 p = a.normalise().stereographicProjection();
	double v = getStereographicVolume( p );
	double l = cubeRoot( v ) * 0.2;
	
	Cube( p, l, Colour( 0.9, 0.6, 0.0 ) );
}



#define MAX_POINTS 1000
#define MAX_LINES  1000

int numPoints = 0;
int numLines = 0;

vec4 points[ MAX_POINTS ];
Pair<int> lines[ MAX_LINES ];




void drawSchlegelShape2()
{
	bool done[256] = {false};

	//  Draw points

	for( int i=0; i<16; i++ )
	{
		int i0 = (i&1) >> 0;
		int i1 = (i&2) >> 1;
		int i2 = (i&4) >> 2;
		int i3 = (i&8) >> 3;

		vec4 a = vec4( 2.0*i0-1.0, 2.0*i1-1.0, 2.0*i2-1.0, 2.0*i3-1.0 );
		drawSchlegalPoint( a );
	}

	//  Draw lines

	for( int j=0; j<16; j++ )
	{
		int j0 = (j&1) >> 0;
		int j1 = (j&2) >> 1;
		int j2 = (j&4) >> 2;
		int j3 = (j&8) >> 3;

		vec4 b = vec4( 2.0*j0-1.0, 2.0*j1-1.0, 2.0*j2-1.0, 2.0*j3-1.0 );

		for( int i=0; i<16; i++ )
		{
			int i0 = (i&1) >> 0;
			int i1 = (i&2) >> 1;
			int i2 = (i&4) >> 2;
			int i3 = (i&8) >> 3;
		
			vec4 a = vec4( 2.0*i0-1.0, 2.0*i1-1.0, 2.0*i2-1.0, 2.0*i3-1.0 );

			int k = i^j;

			if( !done[ (j<<4)+i ] )
			{
				if( k == 1 || k == 2 || k == 4 || k == 8 )
				{
					drawSchlegalThickLine( a, b );
				}

				done[ (j<<4)+i ] = done[ (i<<4)+j ] = true;
			}
		}
	}

}


void drawSchlegelShape( double r[6] )
{
	vec4 *tpoints = new vec4[ numPoints ];

	for( int i=0; i<numPoints; i++ )
	{
		tpoints[i] = points[i].rotate6( r );

		drawSchlegalPoint( tpoints[i] );
	}

	for( int i=0; i<numLines; i++ )
	{
		drawSchlegalThickLine( tpoints[ lines[i].a() ], tpoints[ lines[i].b() ] );
	}

	delete[] tpoints;
}



void defineSchlegelShape()
{
	bool done[256] = {false};

	//  Draw points

	for( int i=0; i<16; i++ )
	{
		int i0 = (i&1) >> 0;
		int i1 = (i&2) >> 1;
		int i2 = (i&4) >> 2;
		int i3 = (i&8) >> 3;

		points[ numPoints++ ] = vec4( 2.0*i0-1.0, 2.0*i1-1.0, 2.0*i2-1.0, 2.0*i3-1.0 );
	}

	//  Draw lines

	for( int j=0; j<16; j++ )
	{
		int j0 = (j&1) >> 0;
		int j1 = (j&2) >> 1;
		int j2 = (j&4) >> 2;
		int j3 = (j&8) >> 3;

		vec4 b = vec4( 2.0*j0-1.0, 2.0*j1-1.0, 2.0*j2-1.0, 2.0*j3-1.0 );

		for( int i=0; i<16; i++ )
		{
			int i0 = (i&1) >> 0;
			int i1 = (i&2) >> 1;
			int i2 = (i&4) >> 2;
			int i3 = (i&8) >> 3;
		
			vec4 a = vec4( 2.0*i0-1.0, 2.0*i1-1.0, 2.0*i2-1.0, 2.0*i3-1.0 );

			int k = i^j;

			if( !done[ (j<<4)+i ] )
			{
				if( k == 1 || k == 2 || k == 4 || k == 8 )
				{
					lines[ numLines++ ] = Pair<int>( i, j );
				}

				done[ (j<<4)+i ] = done[ (i<<4)+j ] = true;
			}
		}
	}

}





void Initialise()
{
  static bool already_called = false;

  if( already_called ) return;

  already_called = true;


  //- Create the shape

  defineSchlegelShape();

  //- Create the texture for the floor

  //  from nehe.06

  glGenTextures(1, &texture[0]);					// Create The Texture

	// Typical Texture Generation Using Data From The Bitmap

	glBindTexture(GL_TEXTURE_2D, texture[0]);
	
  unsigned char data[256*256*3];


  unsigned char c;

  int i=0,j=0;

  for( j=0; j<256; j++ ) for( i=0; i<256; i++ )
  {
    c = i^j;
    data[ (i+256*j)*3+0 ] = c;
    data[ (i+256*j)*3+1 ] = c;
    data[ (i+256*j)*3+2 ] = c;
  }

  glTexImage2D
  (
    GL_TEXTURE_2D,
    0,    //  LOD zero
    3,    //  3 components - red, green, blue
    256,  //  width
    256,  // height
    0,
    GL_RGB,
    GL_UNSIGNED_BYTE,
    data
  );

glEnable(GL_CULL_FACE);

glCullFace(GL_BACK);


  //- global parameters for texturing

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );


	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations


	//- Fog

	GLfloat fogColor[4] = {0,0,0,1.0};
glFogi(GL_FOG_MODE, GL_LINEAR);		// Fog Mode
glFogfv(GL_FOG_COLOR, fogColor);			// Set Fog Color
glFogf(GL_FOG_DENSITY, 0.35f);				// How Dense Will The Fog Be
glHint(GL_FOG_HINT, GL_DONT_CARE);			// Fog Hint Value
glFogf(GL_FOG_START, 25.0f);				// Fog Start Depth
glFogf(GL_FOG_END, 50.0f);				// Fog End Depth
glEnable(GL_FOG);					// Enables GL_FOG


//- Light
/*
GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 0.1f }; 	

GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 0.1f };	

GLfloat LightPosition[]= { 5.0f, 10.0f, -2.0f, 1.0f };


glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);		
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);	
glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);	
glEnable(GL_LIGHT1);	

glEnable( GL_LIGHTING );
*/
}



void ReflectedFloor()
{
	  glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping ( NEW )

    // draw a textured floor
	  glBindTexture(GL_TEXTURE_2D, texture[0]);

    glColor4f( 0.3, 0.3, 0.3, 0.9 );

  //  time = 0;

	double distance_moved = 0;

	for( int i=9; i<12; i++ )
	{
		for( int j=9; j<12; j++ )
		{
			vec3 p1 = vec3( -500 + i*50.0, 0, -500 + j*50.0 );
			vec3 p2 = vec3( -450 + i*50.0, 0, -500 + j*50.0 );
			vec3 p3 = vec3( -450 + i*50.0, 0, -450 + j*50.0 );
			vec3 p4 = vec3( -500 + i*50.0, 0, -450 + j*50.0 );

			if( ! reflectedPointExists( viewPoint, sphereOrigin, sphereRadius, p1 ) ) continue;
			if( ! reflectedPointExists( viewPoint, sphereOrigin, sphereRadius, p2 ) ) continue;
			if( ! reflectedPointExists( viewPoint, sphereOrigin, sphereRadius, p3 ) ) continue;
			if( ! reflectedPointExists( viewPoint, sphereOrigin, sphereRadius, p4 ) ) continue;

			p1 = getReflectedPoint( viewPoint, sphereOrigin, sphereRadius, p1 );
			p2 = getReflectedPoint( viewPoint, sphereOrigin, sphereRadius, p2 );
			p3 = getReflectedPoint( viewPoint, sphereOrigin, sphereRadius, p3 );
			p4 = getReflectedPoint( viewPoint, sphereOrigin, sphereRadius, p4 );

			glBegin(GL_TRIANGLE_FAN);
				glTexCoord2f(  10.0f +i*10.0,  0.0f + j*10.0 ); glVertex3f( p3.x(), p3.y(), p3.z() );
				glTexCoord2f(  10.0f +i*10.0, 10.0f + j*10.0 ); glVertex3f( p2.x(), p2.y(), p2.z() );
				glTexCoord2f(   0.0f +i*10.0, 10.0f + j*10.0 ); glVertex3f( p1.x(), p1.y(), p1.z() );
				glTexCoord2f(   0.0f +i*10.0,  0.0f + j*10.0 ); glVertex3f( p4.x(), p4.y(), p4.z() );
			glEnd();
		}
	}
	  glDisable(GL_TEXTURE_2D);							// Enable Texture Mapping ( NEW )

}




void GameDraw()
{
  int i, j;

  int time = timeGetTime();
  double mx, my, mz;

  mx = 4.3*sin(time*3.14159/7516);
  my = 4.3*sin(time*3.14159/4351);
  mz = 4.3*sin(time*3.14159/6892);

  sphereOrigin = vec3( 0, 4 + 1.5*sin( 3.14159*time/6000 ), 0 );

    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

  double r[6] =
  {
	  2 * time*3.14159 /  7233.0,
	  2 * time*3.14159 /  9215.0,
	  2 * time*3.14159 / 15245.0,
	  2 * time*3.14159 /  6347.0,
	  2 * time*3.14159 / 13746.0,
	  2 * time*3.14159 /  7843.0
  };



//  mx = 4;
//  my = 3;
//  mz = 2;

  ReflectedCube( vec3( -4, 4+mx, mz ), 1, Colour( 0.8, 0.0, 0.0 ) );
  ReflectedCube( vec3( -4, 5.5+mx, mz ), 0.5, Colour( 0.3, 0.6, 0.9 ) );
  ReflectedCube( vec3(  4, 4+my, mx ), 1.5, Colour( 0.0, 0.6, 0.0 ) );

  ReflectedSphere( mx, 4+mz, 5, 1.5 );

  //  Not working yet...
  //ReflectedFloor();

  SphereFloorReflection( sphereOrigin, sphereRadius );

  glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glColor4f( 1, 1, 1, 0.25 );
	Sphere( sphereOrigin.x(), sphereOrigin.y(), sphereOrigin.z(), sphereRadius, false );
  glDisable(GL_BLEND);



  Cube( vec3( -4, 4+mx, mz ), 1, Colour( 0.8, 0.0, 0.0 ) );
  Cube( vec3( -4, 5.5+mx, mz ), 0.5, Colour( 0.3, 0.6, 0.9 ) );
  Cube( vec3(  4, 4+my, mx ), 1.5, Colour( 0.0, 0.6, 0.0 ) );

  Sphere( mx, 4+mz, 5, 1.5 );

/*
  vec3 pos = vec3( 5, 2+my, mx );

  if( reflectedPointExists( viewPoint, sphereOrigin, sphereRadius, pos ) )
	{
		vec3 pos2 = getReflectedPointOnSphere( viewPoint, sphereOrigin, sphereRadius, pos );
		Cube( pos2, 0.1, Colour( 0.8, 0, 0 ) );
	}

  glEnable(GL_BLEND);

  Cube( pos, 1, Colour( 0.8, 0, 0, 0.4 ) );

  glDisable(GL_BLEND);
*/


  
  //- Draw floor 



	  glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping ( NEW )

    // draw a textured floor
	  glBindTexture(GL_TEXTURE_2D, texture[0]);

    glColor4f( 0.7, 0.7, 0.7, 1.0 );

  //  time = 0;

	double distance_moved = 0;


	  glBegin(GL_QUADS);
		  // Front Face
		  glTexCoord2f( -50.0f, -50.0f ); glVertex3f( -500.0f, 0.0f, -500.0f );
		  glTexCoord2f(  50.0f, -50.0f ); glVertex3f(  500.0f, 0.0f, -500.0f );
		  glTexCoord2f(  50.0f,  50.0f ); glVertex3f(  500.0f, 0.0f,  500.0f );
		  glTexCoord2f( -50.0f,  50.0f ); glVertex3f( -500.0f, 0.0f,  500.0f );
	  glEnd();

	  glDisable(GL_TEXTURE_2D);							// Enable Texture Mapping ( NEW )


}





void GameMain()
{
  Initialise();

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glTranslatef(0.f, 0.f, -15.f);

	glRotatef(-r2*0.2, 1.0, 0.0, 0.0);
	glRotatef(-r1*0.2, 0.0, 1.0, 0.0);

	glTranslatef(0.f, -4.f, 0.f);

	viewPoint = getViewPoint();

	GameDraw();
	SwapBuffers (gl_dc);
}
Related Articles

Procedural city block layout

Animated 4-dimensional objects mapped down to 3D

Simulating a cloth falling and slipping over objects

Windows C++ OpenGL example

Games

The Dodge Game
Flatspace

2-Player Games:

Quake 2D
Meteora

Puzzle Games:

Mini Tetris
Sudoku Solver

Development

3D Graphics:

3D Graphics Articles
WebGL Examples
OpenGL Examples
Flash 3D Engine
Java 3D Engine

Development:

Programming Articles
Game Development Examples

Work

Portfolio
Clients
Startups & Projects
Expertise

Links

CubeLogix Studios
PHP Charts & Graphs
Local Legends Football