#!/usr/bin/env python
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import *
import sys

# Some api in the chain is translating the keystrokes to this octal string
# so instead of saying: ESCAPE = 27, we use the following.
ESCAPE = '\033'

# Number of the glut window.
window = 0

rcam = 0.0
xshift = 0

# Rotation angle for the quadrilateral.
rquad = 0.0

# A general OpenGL initialization function.  Sets all of the initial parameters. 
def InitGL(Width, Height):				# We call this right after our OpenGL window is created.
	glClearColor(0.0, 0.0, 0.0, 0.0)	# This Will Clear The Background Color To Black
	glClearDepth(1.0)					# Enables Clearing Of The Depth Buffer
	glDepthFunc(GL_LESS)				# The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST) 			# Enables Depth Testing
	glShadeModel(GL_SMOOTH) 			# Enables Smooth Color Shading
	
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()					# Reset The Projection Matrix
										# Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)

	glMatrixMode(GL_MODELVIEW)

# The function called when our window is resized (which shouldn't happen if you enable fullscreen, below)
def ReSizeGLScene(Width, Height):
	if Height == 0: 					# Prevent A Divide By Zero If The Window Is Too Small 
		Height = 1

	glViewport(0, 0, Width, Height) 	# Reset The Current Viewport And Perspective Transformation
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)
	gluLookAt(0, 0, 0,	0, 0, -10,	0, 10, -10)
	glMatrixMode(GL_MODELVIEW)
	light = 0.9
	glLightfv(GL_LIGHT1, GL_DIFFUSE, (light, light, light, 1))
	glLightfv(GL_LIGHT1, GL_POSITION, (0.0, 0.0, -7.0, 1.0))
	glEnable(GL_LIGHT1)
	glEnable(GL_LIGHTING)
	glTranslatef(0, 0, -20)
	

def DrawKkube(x, y, z, rot):
	glMatrixMode(GL_MODELVIEW)
	glPushMatrix()
#	glLoadIdentity()
	glTranslatef(x, y, z)		# Move Right And Into The Screen
	glRotatef(-rot, 0, 0, 1) 	# Rotate The Cube On X, Y & Z
	color = (0.8, 1.0, 1.0, 1.0)
	glutSolidCube(1.3)
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
	glMaterialf(GL_FRONT, GL_SHININESS, 100.0);
	glMaterialfv(GL_FRONT, GL_EMISSION, (0.2, 0.2, 0.2, 1.0));
	glMaterialfv(GL_FRONT, GL_SPECULAR, color); 
	glPopMatrix()

	
#return the shape point no. i coords
def ShapeCoords(i):
	return (i * 0.2, sin(i/20.0) * (i/100), -7.0)
	
# The main drawing function. 
def DrawGLScene():
	global rquad, rcam, xshift

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); # Clear The Screen And The Depth Buffer
#	gluLookAt(0, rcam, 0,	0, 0, -10,	0, 1, 0)
#	glMatrixMode(GL_MODELVIEW)
#	glLoadIdentity()
#	glRotatef( 0, 0, 1, rcam )
#	glRotatef( 0, 1, 0, 0)
#	glRotatef( 1, 1, 0, 0 )
	xshstep = 0.6
	glTranslatef( -0.2*xshstep, 0, 0)
	xshift = xshift - xshstep
	c = ShapeCoords(xshift)
	rcam = rcam-0.0001#-c[0]
	for i in range(-xshift-100, -xshift+100):
		c = ShapeCoords(i)
		DrawKkube(c[0], c[1] + sin(i), c[2] + cos(i*1.5), rquad + 17*i)
	
	# What values to use?  Well, if you have a FAST machine and a FAST 3D Card, then
	# large values make an unpleasant display with flickering and tearing.	I found that
	# smaller values work better, but this was based on my experience.
	rquad = rquad - 5.0 				# Decrease The Rotation Variable For The Quad

	
	#  since this is double buffered, swap the buffers to display what just got drawn. 
	glutSwapBuffers()

# The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y)	
def keyPressed(*args):
	# If escape is pressed, kill everything.
	if args[0] == ESCAPE:
		glutDestroyWindow(window)
		sys.exit()

def main():
	global window

	glutInit(sys.argv)

	# Select type of Display mode:	 
	#  Double buffer 
	#  RGBA color
	# Alpha components supported 
	# Depth buffer
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
	
	# get a 640 x 480 window 
	glutInitWindowSize(640, 480)
	
	# the window starts at the upper left corner of the screen 
	glutInitWindowPosition(0, 0)
	
	# Okay, like the C version we retain the window id to use when closing, but for those of you new
	# to Python (like myself), remember this assignment would make the variable local and not global
	# if it weren't for the global declaration at the start of main.
	window = glutCreateWindow("Jeff Molofee's GL Code Tutorial ... NeHe '99")

	# Register the drawing function with glut, BUT in Python land, at least using PyOpenGL, we need to
	# set the function pointer and invoke a function to actually register the callback, otherwise it
	# would be very much like the C version of the code.	
	glutDisplayFunc(DrawGLScene)
	#glutDisplayFunc()
	
	# Uncomment this line to get full screen.
	# glutFullScreen()

	# When we are doing nothing, redraw the scene.
	glutIdleFunc(DrawGLScene)
		
	# Register the function called when our window is resized.
	glutReshapeFunc(ReSizeGLScene)
	
	
	# Register the function called when the keyboard is pressed.  
	glutKeyboardFunc(keyPressed)
	
	# Initialize our window. 
	InitGL(640, 480)

	
# Print message to console, and kick off the main to get it rolling.
print "Hit ESC key to quit."

if __name__ == '__main__':
	try:
		GLU_VERSION_1_2
	except:
		print "Need GLU 1.2 to run this demo"
		sys.exit(1)
	main()
	glutMainLoop()
		

