
import OpenGL.GL as gl
import OpenGL.GLU as glu
import OpenGL.GLUT as glut

import glitch
import glitch.limbo.lights
from glitch.limbo.material import Material

glut.glutInit()

class Teapot(glitch.Node):
    def __init__(self, size=1, **kw):
        glitch.Node.__init__(self, **kw)
        self.size = size

    def draw(self, ctx):
        glut.glutSolidTeapot(self.size)

class Square(glitch.Node):
    "A unit square on the XY plane."

    def draw(self, ctx):
        gl.glBegin(gl.GL_QUADS)
        gl.glNormal3f(0, 0, -1)
        gl.glVertex3f(0, 0, 0)
        gl.glVertex3f(1, 0, 0)
        gl.glVertex3f(1, 1, 0)
        gl.glVertex3f(0, 1, 0)
        gl.glEnd()

class Grid(glitch.Node):
    "A 10x10 grid in a unit square on the XY plane."

    def draw(self, ctx):
        gl.glBegin(gl.GL_LINES)

        for x in xrange(11):
            gl.glVertex3f(0.1 * x, 0, 0)
            gl.glVertex3f(0.1 * x, 1, 0)

        for y in xrange(11):
            gl.glVertex3f(0, 0.1 * y, 0)
            gl.glVertex3f(1, 0.1 * y, 0)

        gl.glEnd()

class Mesh(glitch.Node):
    "Like a Grid, but can be textured & illuminated"

    def draw(self, ctx):
        num_subdivisions = 20
        step_size = 1.0 / num_subdivisions
        tex_width= 1.0
        tex_height= 1.0

        for sy in range(num_subdivisions):
            gl.glBegin(gl.GL_TRIANGLE_STRIP)

            gl.glNormal3f(0., 0., 1.)

            y1 = sy * step_size
            y2 = y1 + step_size

            for sx in range(num_subdivisions + 1):

                x1 = sx * step_size

                # XXX: texture upside down to match gstreamer expectations (?)
                gl.glTexCoord2f(x1 * tex_width, tex_height - y1 * tex_height)
                gl.glVertex3f(x1, y1, 0.0)

                gl.glTexCoord2f(x1 * tex_width, tex_height - y2 * tex_height)
                gl.glVertex3f(x1, y2, 0.0)

            gl.glEnd()

class Cube(glitch.Node):
    def __init__(self, size=1, **kw):
        glitch.Node.__init__(self, **kw)
        self.size = size

    def draw(self, ctx):
        # XXX: This is centered around the origin. This is inconsistent with
        # other Glitch objects which fill the unit cube from (0, 0, 0) to (1,
        # 1, 1).
        glut.glutSolidCube(self.size)

class Sphere(glitch.Node):
    def __init__(self, radius=0.5, orientation=glu.GLU_OUTSIDE, **kw):
        glitch.Node.__init__(self, **kw)
        self.radius = radius
        self.orientation = orientation

    def draw(self, ctx):
        quad = glu.gluNewQuadric()
        glu.gluQuadricOrientation(quad, self.orientation)
        # XXX: This is centered around the origin. This is inconsistent with
        # other Glitch objects which fill the unit cube from (0, 0, 0) to (1,
        # 1, 1).
        glu.gluSphere(quad, self.radius, 32, 32)
        glu.gluDeleteQuadric(quad)

class Axes(glitch.Node):
    def axis(self):
        gl.glPushMatrix()

        axis = glu.gluNewQuadric()
        glu.gluQuadricDrawStyle(axis, glu.GLU_FILL)
        glu.gluQuadricNormals(axis, glu.GLU_SMOOTH)
        glu.gluQuadricOrientation(axis, glu.GLU_OUTSIDE)
        glu.gluQuadricTexture(axis, glu.GLU_FALSE)

        glu.gluCylinder(axis, 0.05, 0.05, 1, 10, 1)

        gl.glPushMatrix()
        gl.glTranslate(0, 0, 1)
        glu.gluCylinder(axis, 0.07, 0, 0.1, 10, 1)
        gl.glPopMatrix()

        glu.gluDeleteQuadric(axis)

        gl.glPopMatrix()

    def axes(self):
        gl.glPushMatrix()
        gl.glRotate(90, 0, 1, 0)
        gl.glColor3f(1.0, 0, 0)
        gl.glMaterial(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT,
            [1.0, 0.0, 0.0, 0.8])
        self.axis()
        gl.glPopMatrix()

        gl.glPushMatrix()
        gl.glRotate(-90, 1, 0, 0)
        gl.glColor3f(0, 1.0, 0)
        gl.glMaterial(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT,
            [0.0, 1.0, 0.0, 0.8])
        self.axis()
        gl.glPopMatrix()

        gl.glColor3f(0, 0, 1.0)
        gl.glMaterial(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT,
            [0.0, 0.0, 1.0, 0.8])
        self.axis()

    def draw(self, ctx):
        gl.glPushAttrib(gl.GL_LIGHTING_BIT)
        gl.glDisable(gl.GL_LIGHTING)
        self.axes()
        gl.glPopAttrib()

def make_test_scene():
    """
    Generate a scene with a ground, a sky, and three objects.

    @rtype: A list of nodes.
    @return: The nodes in the scene.
    """

    sky = Material(0.6, 0.7, 1.0, children=[
        Sphere(radius=25, orientation=glu.GLU_INSIDE)])
    ground = glitch.Translate(-30, -1, 30, children=[
        glitch.Scale(60, 1, 60, children=[
        glitch.Rotate(90, -1, children=[
        Material(0.7, 0.6, 0.5, children=[Mesh()])])])])
    teapot = glitch.Translate(-2, children=[
        Material(g=1, children=[Teapot(size=0.5)])])
    cube = glitch.Translate(x=2, children=[
        Material(g=1, children=[Cube()])])
    sphere = glitch.Translate(z=-2, children=[
        Material(g=1, children=[Sphere()])])
    return glitch.Node([sky, ground, teapot, cube, sphere])

def make_lit_test_scene():
    """
    As L{make_test_scene}, but with lighting.

    @rtype: L{Node}
    @return: A node containing the lit scene.
    """

    return glitch.limbo.lights.LightSwitch(children=[
        glitch.limbo.lights.AmbientLight(intensity=0.3, children=[
            glitch.limbo.lights.DiffuseLight(intensity=0.4, children=[
                make_test_scene()])])])

class ColorfulTriangle(glitch.Node):
    def draw(self, ctx):
        gl.glBegin(gl.GL_TRIANGLES)
        gl.glColor3f(1, 0, 0)
        gl.glVertex(-1, 0, 0)
        gl.glColor3f(0, 1, 0)
        gl.glVertex(0, 1, 0)
        gl.glColor3f(0, 0, 1)
        gl.glVertex(1, 0, 0)
        gl.glEnd()

class Cylinder(glitch.Node):
    def __init__(self, base=0.1, top=0.1, height=1.0, slices=20, stacks=1, **kw):
        glitch.Node.__init__(self, **kw)
        (self.base, self.top, self.height, self.slices, self.stacks) = (base, top, height, slices, stacks)

    def draw(self, ctx):
        quad = glu.gluNewQuadric()
        glu.gluCylinder(quad, self.base, self.top, self.height, self.slices, self.stacks)
        glu.gluDeleteQuadric(quad)

