# PyEPL: hardware/vr/environment.pyx
#
# Copyright (C) 2003-2005 Michael J. Kahana
# Authors: Ian Schleifer, Per Sederberg, Aaron Geller, Josh Jacobs
# URL: http://memory.psych.upenn.edu/programming/pyepl
#
# Distributed under the terms of the GNU Lesser General Public License
# (LGPL). See the license.txt that came with this file.

from OpenGL.GL import glGenLists, glNewList, glEndList, glEnable, glDisable, glViewport, glMatrixMode, glLoadIdentity, glClear, glRotatef, glTranslatef, glCallLists, glBindTexture, glBegin, glTexCoord2f, glVertex3f, glEnd, GL_COMPILE, GL_TEXTURE_2D, GL_PROJECTION, GL_MODELVIEW, GL_DEPTH_BUFFER_BIT, GL_QUADS, glCallList, GL_DEPTH_TEST, GL_TRUE, glPushMatrix, glPopMatrix, glDeleteLists, GL_CULL_FACE, glCullFace, GL_FRONT, glGetFloatv, GL_MODELVIEW_MATRIX, GL_TRIANGLE_STRIP, glVertex2f, GL_BLEND, glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, glFogi, glFogf, glFogfv, GL_FOG, GL_FOG_MODE, GL_FOG_DENSITY, GL_FOG_START, GL_FOG_END, GL_FOG_COLOR, GL_LINEAR, GL_EXP, GL_EXP2, glHint, GL_FOG_HINT, GL_NICEST, glColor4f
from OpenGL.GLU import gluPerspective, gluSphere, gluNewQuadric, gluQuadricNormals, gluQuadricTexture, GLU_SMOOTH, gluQuadricOrientation, GLU_INSIDE

import ode
from pyepl.hardware.timing import universal_time
from numpy import reshape, asarray

cdef object collattrs
cdef float maxfacetlength

# OSX glLoadMatrixf fix
import OpenGL.GL
cdef object glLoadMatrixf
if hasattr(OpenGL.GL,"glLoadMatrixf"):
    glLoadMatrixf = OpenGL.GL.glLoadMatrixf
else:
    glLoadMatrixf = OpenGL.GL.glLoadMatrix
    
collattrs = [
    ("mu", "setMu", 0),
    ("mu2", "setMu2", ode.ContactMu2),
    ("bounce", "setBounce", ode.ContactBounce),
    ("bounce_vel", "setBounceVel", ode.ContactBounce),
    ("soft_erp", "setSoftERP", ode.ContactSoftERP),
    ("soft_cfm", "setSoftCFM", ode.ContactSoftCFM),
    ("motion1", "setMotion1", ode.ContactMotion1),
    ("motion2", "setMotion2", ode.ContactMotion2),
    ("slip1", "setSlip1", ode.ContactSlip1),
    ("slip2", "setSlip2", ode.ContactSlip2),
    ("contact_approx1_1", None, ode.ContactApprox1_1),
    ("contact_approx1_2", None, ode.ContactApprox1_2),
    ("contact_approx1", None, ode.ContactApprox1)
    ]

def setMaxFacetLength(float x):
    global maxfacetlength
    maxfacetlength = x

cdef class VREntity:
    pass

cdef class VRShape(VREntity):
    def getODEGeometries(self, world, space):  # To be overridden
        pass

cdef class VRVisible(VREntity):
    def prepare(self):  # To be overridden
        pass
    def construct(self):  # To be overridden
        pass
    def clean(self):  # To be overridden
        pass

cdef class VRDynamic(VREntity):
    def liveConstruct(self, float x, float y, float z, float yaw, float pitch, float roll, float fovy, float aspect, int width, int height):  # To be overridden
        pass

cdef class LowVEnvironment:
    cdef object entities
    cdef object gl_lists
    cdef object gl_live_constructs
    cdef object ode_geometries
    cdef readonly object world
    cdef object contactgroup
    cdef readonly object space
    cdef object ode_to_remove
    cdef object ode_to_add
    cdef object laststep
    cdef int stepping
    cdef int fogmode
    cdef object fogcolor
    cdef float fogfar
    cdef float fognear
    cdef float fogdensity
    def __init__(self):
        """
        """
        self.entities = {}
        self.gl_lists = []
        self.gl_live_constructs = []
        self.fogmode = 0
        
        # create ODE world...
        self.world = ode.World()
        self.contactgroup = ode.JointGroup()
        self.space = ode.Space()
        self.ode_to_remove = []
        self.ode_to_add = []
        self.laststep = None
        self.stepping = 0
    def __del__(self):
        """
        """
        for entity in self.entities.keys():
            self.removeEntity(entity)
    def addEntity(self, entity):
        """
        """
        if isinstance(entity, VRShape):
            self.ode_to_add.append(entity)
        geoms = []
        if isinstance(entity, VRVisible):
            listnum = glGenLists(1)
            entity.prepare()
            glNewList(listnum, GL_COMPILE)
            entity.construct()
            glEndList()
        else:
            listnum = None
        if isinstance(entity, VRDynamic):
            self.gl_live_constructs.append(entity)
        self.entities[entity] = (listnum, geoms)
        if not listnum is None:
            self.gl_lists.append(listnum)
        return entity
    def removeEntity(self, entity):
        """
        """
        listnum, geoms = self.entities.pop(entity)
        if not listnum is None:
            glDeleteLists(listnum, 1)
            self.gl_lists.remove(listnum)
        for geom in geoms:
            self.ode_to_remove.append(geom)
        if entity in self.gl_live_constructs:
            self.gl_live_constructs.remove(entity)
    def render(self, float x, float y, float z, float yaw, float pitch, float roll, float fovy, float aspect, float zNear, float zFar, int lowerleftx, int lowerlefty, int width, int height):
        """
        """
        cdef object i

        glEnable(GL_TEXTURE_2D)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        #glCullFace(GL_FRONT)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        if self.fogmode:
            glFogi(GL_FOG_MODE, self.fogmode)
            glFogf(GL_FOG_DENSITY, self.fogdensity)
            glFogf(GL_FOG_START, self.fognear)
            glFogf(GL_FOG_END, self.fogfar)
            glFogfv(GL_FOG_COLOR, self.fogcolor)
            glHint(GL_FOG_HINT, GL_NICEST)
            glEnable(GL_FOG)
        else:
            glDisable(GL_FOG)
        glViewport(lowerleftx, lowerlefty, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(fovy, aspect, zNear, zFar)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glClear(GL_DEPTH_BUFFER_BIT)

        glRotatef(yaw, 0.0, -1.0, 0.0)
        glRotatef(pitch, -1.0, 0.0, 0.0)
        glRotatef(roll, 0.0, 0.0, -1.0)
        glTranslatef(-x, -y, -z)
        #for i in self.gl_lists:
        #    glCallList(i)
        glCallLists(self.gl_lists)
        for i in self.gl_live_constructs:
            i.liveConstruct(x, y, z, yaw, pitch, roll, fovy, aspect, width, height)
    def near_callback(self, args, geom1, geom2):
        """
        """
        global collattrs
        cdef object contacts
        cdef object world
        cdef object contactgroup
        cdef long mode
        cdef object x
        cdef object attrname
        cdef object setfuncname
        cdef long modebit
        contacts=ode.collide(geom1,geom2)

        if not len(contacts):
            return

        # Callbacks:
        try:
            if hasattr(geom1, "callback"):
                try:
                    cbargs = geom1.cbargs
                except AttributeError:
                    cbargs = ()
                geom1.callback(*cbargs)
            if hasattr(geom2, "callback"):
                try:
                    cbargs = geom2.cbargs
                except AttributeError:
                    cbargs = ()
                geom2.callback(*cbargs)
        except:
            import traceback
            traceback.print_exc()

        if (hasattr(geom1, "permeable") and geom1.permeable) or (hasattr(geom2, "permeable") and geom2.permeable):
            return

        #create the contact joints
        world, contactgroup = args
        for c in contacts:
            mode = 0
            for attrname, setfuncname, modebit in collattrs:
                try:
                    x = getattr(geom1, attrname)
                    mode = mode | modebit
                    if setfuncname:
                        getattr(c, setfuncname)(x)
                except AttributeError:
                    try:
                        x = getattr(geom2, attrname)
                        mode = mode | modebit
                        if setfuncname:
                            getattr(c, setfuncname)(x)
                    except AttributeError:
                        pass
            c.setMode(mode)
            j=ode.ContactJoint(world, contactgroup, c)
            j.attach(geom1.getBody(), geom2.getBody())
    cdef updateGeoms(self):
        cdef object geoms
        cdef object geom
        cdef object x
        for geom in self.ode_to_remove:
            # this is ugly but necesssary if we're going to use
            # plain PyODE
            for key in ode._geom_c2py_lut.keys():
                if ode._geom_c2py_lut[key]==geom:	        
                    del ode._geom_c2py_lut[key]
        for x in self.ode_to_add:
            geoms = x.getODEGeometries(self.world, self.space)
            self.entities[x] = (self.entities[x][0], geoms)
        self.ode_to_remove = []
        self.ode_to_add = []
    def dynamicStep(self, float substep):
        """
        Update the dynamics simulation by the amount of time specified.
        """
        cdef int x
        cdef object steptime
        cdef float milliseconds
        if self.stepping:
            return
        self.stepping = 1
        self.updateGeoms()
        steptime = universal_time()
        if self.laststep:
            milliseconds = steptime - self.laststep
        else:
            milliseconds = 0.0
            self.laststep = steptime
        if milliseconds:
            self.laststep = steptime
            while milliseconds > 0.0:
                self.space.collide((self.world, self.contactgroup), self.near_callback)
                self.updateGeoms()
                self.world.step(min(milliseconds, substep))
                self.contactgroup.empty()
                milliseconds = milliseconds - substep
        self.stepping = 0
    def setGravity(self, x, y, z):
        """
        """
        self.world.setGravity((x, y, z))
    def setFog(self, mode = None, color = (0.5, 0.5, 0.5), far = 1.0, near = 0.0, density = 1.0):
        if mode is None:
            mode = 0
        elif mode == "linear":
            mode = GL_LINEAR
        elif mode == "exponential":
            mode = GL_EXP
        elif mode == "exponential^2":
            mode = GL_EXP2
        else:
            raise ValueError, "Invalid fog mode: %r" % mode
        self.fogmode = mode
        self.fogcolor = color
        self.fogfar = far
        self.fognear = near
        self.fogdensity = density

cdef class SphereGeom(VRShape):
    """
    """
    cdef float x
    cdef float y
    cdef float z
    cdef float radius
    cdef object surface_options
    def __init__(self, x, y, z, radius, **surface_options):
        """
        """
        self.x = x
        self.y = y
        self.z = z
        self.radius = radius
        self.surface_options = surface_options
    def getODEGeometries(self, world, space):
        """
        """
        #make a geom box for collision detection
        geom=ode.GeomSphere(space, self.radius)
        geom.setBody(None)
        
        geom.setPosition((self.x, self.y, self.z))
        geom.setCategoryBits(0x00000001)
        geom.setCollideBits(long('0xFFFFFFFE', 16))
        
        for key, value in self.surface_options.iteritems():
            setattr(geom, key, value)
        return (geom,)

cdef class BoxGeom(VRShape):
    """
    """
    cdef float x
    cdef float y
    cdef float z
    cdef float xsize
    cdef float ysize
    cdef float zsize
    cdef object surface_options
    def __init__(self, x, y, z, xsize, ysize, zsize, **surface_options):
        """
        """
        self.x = x
        self.y = y
        self.z = z
        self.xsize = xsize
        self.ysize = ysize
        self.zsize = zsize
        self.surface_options = surface_options
    def getODEGeometries(self, world, space):
        """
        """
        #make a geom box for collision detection
        geom=ode.GeomBox(space, (self.xsize, self.ysize, self.zsize))
        geom.setBody(None)
        
        geom.setPosition((self.x, self.y, self.z))
        geom.setCategoryBits(0x00000001)
        geom.setCollideBits(long('0xFFFFFFFE', 16))
        
        for key, value in self.surface_options.iteritems():
            setattr(geom, key, value)
        return (geom,)

cdef class PlaneGeom(VRShape):
    """
    """
    cdef float a
    cdef float b
    cdef float c
    cdef float d
    cdef object surface_options
    def __init__(self, a, b, c, d, **surface_options):
        """
        """
        self.a = a
        self.b = b
        self.c = c
        self.d = d
        self.surface_options = surface_options
    def getODEGeometries(self, world, space):
        """
        """
        #make a geom box for collision detection
        geom=ode.GeomPlane(space, (self.a, self.b, self.c), self.d)

        geom.setCategoryBits(0x00000001)
        geom.setCollideBits(long('0xFFFFFFFE', 16))
        
        for key, value in self.surface_options.iteritems():
            setattr(geom, key, value)
        return (geom,)

cdef class Sphere(VRVisible):
    """
    """
    cdef object image
    cdef float radius
    cdef int slices
    cdef int stacks
    cdef float x
    cdef float y
    cdef float z
    def __init__(self, x, y, z, image, radius, slices = 16, stacks = 16):
        self.image = image
        self.radius = radius
        self.slices = slices
        self.stacks = stacks
        self.x = x
        self.y = y
        self.z = z
    def construct(self):
        cdef object quad
        quad = gluNewQuadric()
        gluQuadricNormals(quad, GLU_SMOOTH)
        gluQuadricTexture(quad, GL_TRUE)
        gluQuadricOrientation(quad, GLU_INSIDE)
        glPushMatrix()
        glTranslatef(self.x, self.y, self.z)
        glBindTexture(GL_TEXTURE_2D, self.image.gl_texture.texid)
        gluSphere(quad, self.radius, self.slices, self.stacks)
        glPopMatrix()

cdef class Sprite(VRDynamic):
    """
    """
    cdef float x
    cdef float y
    cdef float z
    cdef object texture
    cdef object image
    cdef float lowx
    cdef float highx
    cdef float lowy
    cdef float highy
    def __init__(self, float x, float y, float z, image, float xsize, float ysize):
        """
        """
        self.x = x
        self.y = y
        self.z = z
        self.image = image
        self.texture = image.gl_texture.texid
        self.highx = xsize / 2.0
        self.highy = ysize / 2.0
        lowx = -self.highx
        lowy = -self.highy
    def liveConstruct(self, float x, float y, float z, float yaw, float pitch, float roll, float fovy, float aspect, int width, int height):
        """
        """
        cdef object modelview
        cdef int i
        cdef int j
        glPushMatrix()
        glTranslatef(self.x, self.y, self.z)
        modelview = glGetFloatv(GL_MODELVIEW_MATRIX)
        if isinstance(modelview, list):
            modelview = asarray(modelview)
        for i from 0 <= i < 3:
            for j from 0 <= j < 3:
                if i == j:
                    modelview[i, j] = 1.0
                else:
                    modelview[i, j] = 0.0
        glLoadMatrixf(reshape(modelview, (16,)))
        glBindTexture(GL_TEXTURE_2D, self.texture)
        glBegin(GL_QUADS)
        glTexCoord2f(0.0, 1.0); glVertex2f(self.lowx, self.lowy)
        glTexCoord2f(1.0, 1.0); glVertex2f(self.highx, self.lowy)
        glTexCoord2f(1.0, 0.0); glVertex2f(self.highx, self.highy)
        glTexCoord2f(0.0, 0.0); glVertex2f(self.lowx, self.highy)
        glEnd()
        glPopMatrix()

cdef xzRect(float lowx, float lowz, float highx, float highz, float y, float xtiletimes, float ztiletimes, float maxfacet, object order, object texorder):
    cdef float xsize
    cdef float zsize
    cdef int xfacets
    cdef int zfacets
    cdef float xfacetsize
    cdef float zfacetsize
    cdef int x
    cdef int z
    cdef float texlowx
    cdef float texhighx
    cdef float texlowz
    cdef float texhighz
    cdef float texpatchsizex
    cdef float texpatchsizez
    cdef object corners
    xsize = highx - lowx
    zsize = highz - lowz
    if maxfacet:
        xfacets = int((xsize / maxfacet) + 1)
        zfacets = int((zsize / maxfacet) + 1)
    else:
        xfacets = 1
        zfacets = 1
    xfacetsize = xsize / xfacets
    zfacetsize = zsize / zfacets
    texpatchsizex = xtiletimes / xfacets
    texpatchsizez = ztiletimes / zfacets
    for x from 0 <= x < xfacets:
         for z from 0 <= z < zfacets:
            corners = [
                (lowx + (xfacetsize * x), y, lowz + (zfacetsize * z)),
                (lowx + (xfacetsize * x), y, lowz + (zfacetsize * (z + 1))),
                (lowx + (xfacetsize * (x + 1)), y, lowz + (zfacetsize * z)),
                (lowx + (xfacetsize * (x + 1)), y, lowz + (zfacetsize * (z + 1)))
                ]
            texlowx = x * texpatchsizex
            texhighx = texlowx + texpatchsizex
            texlowz = z * texpatchsizez
            texhighz = texlowz + texpatchsizez
            texcorners = [
                (texhighx, texlowz),
                (texlowx, texlowz),
                (texlowx, texhighz),
                (texhighx, texhighz)
            ]
            glTexCoord2f(*texcorners[texorder[0]]); glVertex3f(*corners[order[0]])
            glTexCoord2f(*texcorners[texorder[1]]); glVertex3f(*corners[order[1]])
            glTexCoord2f(*texcorners[texorder[2]]); glVertex3f(*corners[order[2]])
            glTexCoord2f(*texcorners[texorder[3]]); glVertex3f(*corners[order[3]])

cdef xyRect(float lowx, float lowy, float highx, float highy, float z, float xtiletimes, float ytiletimes, float maxfacet, object order, object texorder):
    cdef float xsize
    cdef float ysize
    cdef int xfacets
    cdef int yfacets
    cdef float xfacetsize
    cdef float yfacetsize
    cdef int x
    cdef int y
    cdef float texlowx
    cdef float texhighx
    cdef float texlowy
    cdef float texhighy
    cdef float texpatchsizex
    cdef float texpatchsizey
    cdef object corners
    xsize = highx - lowx
    ysize = highy - lowy
    if maxfacet:
        xfacets = int((xsize / maxfacet) + 1)
        yfacets = int((ysize / maxfacet) + 1)
    else:
        xfacets = 1
        yfacets = 1
    xfacetsize = xsize / xfacets
    yfacetsize = ysize / yfacets
    texpatchsizex = xtiletimes / xfacets
    texpatchsizey = ytiletimes / yfacets
    for x from 0 <= x < xfacets:
        for y from 0 <= y < yfacets:
            corners = [
                (lowx + (xfacetsize * x), lowy + (yfacetsize * y), z),
                (lowx + (xfacetsize * x), lowy + (yfacetsize * (y + 1)), z),
                (lowx + (xfacetsize * (x + 1)), lowy + (yfacetsize * y), z),
                (lowx + (xfacetsize * (x + 1)), lowy + (yfacetsize * (y + 1)), z)
                ]
            texlowx = x * texpatchsizex
            texhighx = texlowx + texpatchsizex
            texlowy = y * texpatchsizey
            texhighy = texlowy + texpatchsizey
            texcorners = [
                (texhighx, texlowy),
                (texlowx, texlowy),
                (texlowx, texhighy),
                (texhighx, texhighy)
            ]
            glTexCoord2f(*texcorners[texorder[0]]); glVertex3f(*corners[order[0]])
            glTexCoord2f(*texcorners[texorder[1]]); glVertex3f(*corners[order[1]])
            glTexCoord2f(*texcorners[texorder[2]]); glVertex3f(*corners[order[2]])
            glTexCoord2f(*texcorners[texorder[3]]); glVertex3f(*corners[order[3]])

cdef yzRect(float lowy, float lowz, float highy, float highz, float x, float ytiletimes, float ztiletimes, float maxfacet, object order, object texorder):
    cdef float ysize
    cdef float zsize
    cdef int yfacets
    cdef int zfacets
    cdef float yfacetsize
    cdef float zfacetsize
    cdef int y
    cdef int z
    cdef float texlowy
    cdef float texhighy
    cdef float texlowz
    cdef float texhighz
    cdef float texpatchsizey
    cdef float texpatchsizez
    cdef object corners
    ysize = highy - lowy
    zsize = highz - lowz
    if maxfacet:
        yfacets = int((ysize / maxfacet) + 1)
        zfacets = int((zsize / maxfacet) + 1)
    else:
        yfacets = 1
        zfacets = 1
    yfacetsize = ysize / yfacets
    zfacetsize = zsize / zfacets
    texpatchsizey = ytiletimes / yfacets
    texpatchsizez = ztiletimes / zfacets
    for y from 0 <= y < yfacets:
        for z from 0 <= z < zfacets:
            corners = [
                (x, lowy + (yfacetsize * y), lowz + (zfacetsize * z)),
                (x, lowy + (yfacetsize * y), lowz + (zfacetsize * (z + 1))),
                (x, lowy + (yfacetsize * (y + 1)), lowz + (zfacetsize * z)),
                (x, lowy + (yfacetsize * (y + 1)), lowz + (zfacetsize * (z + 1)))
                ]
            texlowy = y * texpatchsizey
            texhighy = texlowy + texpatchsizey
            texlowz = z * texpatchsizez
            texhighz = texlowz + texpatchsizez
            texcorners = [
                (texhighz, texlowy),
                (texlowz, texlowy),
                (texlowz, texhighy),
                (texhighz, texhighy)
            ]
            glTexCoord2f(*texcorners[texorder[0]]); glVertex3f(*corners[order[0]])
            glTexCoord2f(*texcorners[texorder[1]]); glVertex3f(*corners[order[1]])
            glTexCoord2f(*texcorners[texorder[2]]); glVertex3f(*corners[order[2]])
            glTexCoord2f(*texcorners[texorder[3]]); glVertex3f(*corners[order[3]])

cdef class SkyBox(VRVisible):
    """
    """
    cdef float x
    cdef float y
    cdef float z
    cdef float xsize
    cdef float ysize
    cdef float zsize
    cdef object image
    cdef float xwalltiletimes
    cdef float ywalltiletimes
    def __init__(self, image, x = 0.0, y = 0.0, z = 0.0, xsize = 500.0, ysize = 500.0, zsize = 500.0, texlen = 500.0):
        """
        """
        self.x = x
        self.y = y
        self.z = z
        self.xsize = xsize
        self.ysize = ysize
        self.zsize = zsize
        self.image = image
        self.xwalltiletimes = xsize / texlen
        self.ywalltiletimes = ysize / texlen
    def construct(self):
        global maxfacetlength
        cdef float lowx
        cdef float highx
        cdef float lowy
        cdef float highy
        cdef float lowz
        cdef float highz
        cdef float halfxsize
        cdef float halfzsize
        halfxsize = self.xsize / 2.0
        halfzsize = self.zsize / 2.0
        lowx = self.x - halfxsize
        highx = self.x + halfxsize
        lowy = self.y
        highy = self.y + self.ysize
        lowz = self.z - halfzsize
        highz = self.z + halfzsize
        # Walls...
        glBindTexture(GL_TEXTURE_2D, self.image.gl_texture.texid)
        glBegin(GL_QUADS)
        # Front face
        xyRect(lowx, lowy, highx, highy, lowz, self.xwalltiletimes, self.ywalltiletimes, maxfacetlength, [3, 1, 0, 2], [3, 2, 1, 0])
        # Rear face
        xyRect(lowx, lowy, highx, highy, highz, self.xwalltiletimes, self.ywalltiletimes, maxfacetlength, [1, 3, 2, 0], [2, 3, 0, 1])
        # Left face
        yzRect(lowy, lowz, highy, highz, lowx, self.xwalltiletimes, self.ywalltiletimes, maxfacetlength, [2, 3, 1, 0], [2, 3, 0, 1])
        # Right face
        yzRect(lowy, lowz, highy, highz, highx, self.xwalltiletimes, self.ywalltiletimes, maxfacetlength, [3, 2, 0, 1], [3, 2, 1, 0])
        glEnd()

cdef class FloorBox(VRVisible):
    """
    """
    cdef float x
    cdef float y
    cdef float z
    cdef float xsize
    cdef float ysize
    cdef float zsize
    cdef object floorimage
    cdef float xfloortiletimes
    cdef float yfloortiletimes
    cdef object wallimage
    cdef float xwalltiletimes
    cdef float ywalltiletimes
    def __init__(self, x, y, z, xsize, ysize, zsize, floorimage, floortexlen = None, wallimage = None, walltexlen = None):
        """
        """
        self.x = x
        self.y = y
        self.z = z
        self.xsize = xsize
        self.ysize = ysize
        self.zsize = zsize
        self.floorimage = floorimage
        if floortexlen:
            self.xfloortiletimes = xsize / floortexlen
            self.yfloortiletimes = zsize / floortexlen
        else:
            self.xfloortiletimes = 1.0
            self.yfloortiletimes = 1.0
        self.wallimage = wallimage
        if walltexlen:
            self.xwalltiletimes = xsize / walltexlen
            self.ywalltiletimes = ysize / walltexlen
        else:
            self.xwalltiletimes = 1.0
            self.ywalltiletimes = 1.0
    def construct(self):
        global maxfacetlength
        cdef float lowx
        cdef float highx
        cdef float lowy
        cdef float highy
        cdef float lowz
        cdef float highz
        cdef float halfxsize
        cdef float halfzsize
        cdef int x
        cdef int y
        halfxsize = self.xsize / 2.0
        halfzsize = self.zsize / 2.0
        lowx = self.x - halfxsize
        highx = self.x + halfxsize
        lowy = self.y
        highy = self.y + self.ysize
        lowz = self.z - halfzsize
        highz = self.z + halfzsize
        # Floor...
        glBindTexture(GL_TEXTURE_2D, self.floorimage.gl_texture.texid)
        glBegin(GL_QUADS)
        xzRect(lowx, lowz, highx, highz, lowy, self.xfloortiletimes, self.yfloortiletimes, maxfacetlength, [1, 3, 2, 0], [0, 1, 2, 3])
        glEnd()
        
        # Walls...
        if self.wallimage:
            glBindTexture(GL_TEXTURE_2D, self.wallimage.gl_texture.texid)
            glBegin(GL_QUADS)
            # Front face
            xyRect(lowx, lowy, highx, highy, lowz, self.xwalltiletimes, self.ywalltiletimes, maxfacetlength, [3, 1, 0, 2], [0, 1, 2, 3])
            # Rear face
            xyRect(lowx, lowy, highx, highy, highz, self.xwalltiletimes, self.ywalltiletimes, maxfacetlength, [1, 3, 2, 0], [2, 3, 0, 1])
            # Left face
            yzRect(lowy, lowz, highy, highz, lowx, self.ywalltiletimes, self.xwalltiletimes, maxfacetlength, [2, 3, 1, 0], [2, 3, 0, 1])
            # Right face
            yzRect(lowy, lowz, highy, highz, highx, self.ywalltiletimes, self.xwalltiletimes, maxfacetlength, [3, 2, 0, 1], [0, 1, 2, 3])
            glEnd()

cdef class BuildingBox(VRVisible):
    """
    """
    cdef float x
    cdef float y
    cdef float z
    cdef object image
    cdef float width
    cdef float height
    cdef object roofimage
    cdef float xrooftiletimes
    cdef float xtiletimes
    cdef float yrooftiletimes
    cdef float ytiletimes
    def __init__(self, x, y, z, image, width, height, roofimage = None, rooftexlen = None, texlen = None):
        """
        """
        self.x = x
        self.y = y
        self.z = z
        self.image = image
        self.width = width
        self.height = height
        self.roofimage = roofimage
        if rooftexlen:
            self.xrooftiletimes = width / rooftexlen
            self.yrooftiletimes = width / rooftexlen
        else:
            self.xrooftiletimes = 1.0
            self.yrooftiletimes = 1.0
        if texlen:
            self.xtiletimes = height / texlen
            self.ytiletimes = height / texlen
        else:
            self.xtiletimes = 1.0
            self.ytiletimes = 1.0
    def construct(self):
        cdef float lowx
        cdef float highx
        cdef float lowy
        cdef float highy
        cdef float lowz
        cdef float highz
        cdef float halfwidth
        halfwidth = self.width / 2.0
        lowx = self.x - halfwidth
        highx = self.x + halfwidth
        lowy = self.y
        highy = self.y + self.height
        lowz = self.z - halfwidth
        highz = self.z + halfwidth
        # Walls...
        glBindTexture(GL_TEXTURE_2D, self.image.gl_texture.texid)
        glBegin(GL_QUADS)
        # Front face
        xyRect(lowx, lowy, highx, highy, lowz, self.xtiletimes, self.ytiletimes, maxfacetlength, [1, 3, 2, 0], [0, 1, 2, 3])
        # Rear face
        xyRect(lowx, lowy, highx, highy, highz, self.xtiletimes, self.ytiletimes, maxfacetlength, [3, 1, 0, 2], [0, 1, 2, 3])
        # Left face
        yzRect(lowy, lowz, highy, highz, lowx, self.ytiletimes, self.xtiletimes, maxfacetlength, [3, 2, 0, 1], [0, 1, 2, 3])
        # Right face
        yzRect(lowy, lowz, highy, highz, highx, self.ytiletimes, self.xtiletimes, maxfacetlength, [2, 3, 1, 0], [0, 1, 2, 3])
        glEnd()
        # Roof...
        if self.roofimage:
            glBindTexture(GL_TEXTURE_2D, self.roofimage.gl_texture.texid)
            glBegin(GL_QUADS)
            xzRect(lowx, lowz, highx, highz, highy, self.xrooftiletimes, self.yrooftiletimes, maxfacetlength, [1, 3, 2, 0], [0, 1, 2, 3])
            glEnd()
