text: """Copyright (c) 2010-2012 David Rio Vierra
  
  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.
  
  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
  
  
  """
  renderer.py
  
  What is going on in this file?
  
  Here is an attempt to show the relationships between classes and
  their responsibilities
  
  MCRenderer:
      has "position", "origin", optionally "viewFrustum"
      Loads chunks near position+origin, draws chunks offset by origin
      Calls visible on viewFrustum to exclude chunks
  
  
      (+) ChunkRenderer
          Has "chunkPosition", "invalidLayers", "lists"
          One per chunk and detail level.
          Creates display lists from BlockRenderers
  
          (*) BlockRenderer
              Has "vertexArrays"
              One per block type, plus one for low detail and one for Entity
  """
  
  from collections import defaultdict, deque
  from datetime import datetime, timedelta
  from depths import DepthOffset
  from glutils import gl, Texture
  import logging
  import numpy
  from OpenGL import GL
  import pymclevel
  import sys
  #import time
  
  
  def chunkMarkers(chunkSet):
      """ Returns a mapping { size: [position, ...] } for different powers of 2
      as size.
      """
  
      sizedChunks = defaultdict(list)
      size = 1
  
      def all4(cx, cz):
          cx &= ~size
          cz &= ~size
          return [(cx, cz), (cx + size, cz), (cx + size, cz + size), (cx, cz + size)]
  
      # lastsize = 6
      size = 1
      while True:
          nextsize = size << 1
          chunkSet = set(chunkSet)
          while len(chunkSet):
              cx, cz = chunkSet.pop()
              chunkSet.add((cx, cz))
              o = all4(cx, cz)
              others = set(o).intersection(chunkSet)
              if len(others) == 4:
                  sizedChunks[nextsize].append(o[0])
                  for c in others:
                      chunkSet.discard(c)
              else:
                  for c in others:
                      sizedChunks[size].append(c)
                      chunkSet.discard(c)
  
          if len(sizedChunks[nextsize]):
              chunkSet = set(sizedChunks[nextsize])
              sizedChunks[nextsize] = []
              size <<= 1
          else:
              break
      return sizedChunks
  
  
  class ChunkRenderer(object):
      maxlod = 2
      minlod = 0
  
      def __init__(self, renderer, chunkPosition):
          self.renderer = renderer
          self.blockRenderers = []
          self.detailLevel = 0
          self.invalidLayers = set(Layer.AllLayers)
  
          self.chunkPosition = chunkPosition
          self.bufferSize = 0
          self.renderstateLists = None
  
      @property
      def visibleLayers(self):
          return self.renderer.visibleLayers
  
      def forgetDisplayLists(self, states=None):
          if self.renderstateLists is not None:
              # print "Discarded {0}, gained {1} bytes".format(self.chunkPosition,self.bufferSize)
  
              for k in states or self.renderstateLists.iterkeys():
                  a = self.renderstateLists.get(k, [])
                  # print a
                  for i in a:
                      gl.glDeleteLists(i, 1)
  
              if states:
                  del self.renderstateLists[states]
              else:
                  self.renderstateLists = None
  
              self.needsRedisplay = True
              self.renderer.discardMasterList()
  
      def debugDraw(self):
          for blockRenderer in self.blockRenderers:
              blockRenderer.drawArrays(self.chunkPosition, False)
  
      def makeDisplayLists(self):
          if not self.needsRedisplay:
              return
          self.forgetDisplayLists()
          if not self.blockRenderers:
              return
  
          lists = defaultdict(list)
  
          showRedraw = self.renderer.showRedraw
  
          if not (showRedraw and self.needsBlockRedraw):
              GL.glEnableClientState(GL.GL_COLOR_ARRAY)
  
          renderers = self.blockRenderers
  
          for blockRenderer in renderers:
              if self.detailLevel not in blockRenderer.detailLevels:
                  continue
              if blockRenderer.layer not in self.visibleLayers:
                  continue
  
              l = blockRenderer.makeArrayList(self.chunkPosition, self.needsBlockRedraw and showRedraw)
              lists[blockRenderer.renderstate].append(l)
  
          if not (showRedraw and self.needsBlockRedraw):
              GL.glDisableClientState(GL.GL_COLOR_ARRAY)
  
          self.needsRedisplay = False
          self.renderstateLists = lists
  
      @property
      def needsBlockRedraw(self):
          return Layer.Blocks in self.invalidLayers
  
      def invalidate(self, layers=None):
          if layers is None:
              layers = Layer.AllLayers
  
          if layers:
              layers = set(layers)
              self.invalidLayers.update(layers)
              blockRenderers = [br for br in self.blockRenderers
                                if br.layer is Layer.Blocks
                                or br.layer not in layers]
              if len(blockRenderers) < len(self.blockRenderers):
                  self.forgetDisplayLists()
              self.blockRenderers = blockRenderers
  
              if self.renderer.showRedraw and Layer.Blocks in layers:
                  self.needsRedisplay = True
  
      def calcFaces(self):
          minlod = self.renderer.detailLevelForChunk(self.chunkPosition)
  
          minlod = min(minlod, self.maxlod)
          if self.detailLevel != minlod:
              self.forgetDisplayLists()
              self.detailLevel = minlod
              self.invalidLayers.add(Layer.Blocks)
  
              # discard the standard detail renderers
              if minlod > 0:
                  blockRenderers = []
                  for br in self.blockRenderers:
                      if br.detailLevels != (0,):
                          blockRenderers.append(br)
  
                  self.blockRenderers = blockRenderers
  
          if self.renderer.chunkCalculator:
              for i in self.renderer.chunkCalculator.calcFacesForChunkRenderer(self):
                  yield
  
          else:
              raise StopIteration
              yield
  
      def vertexArraysDone(self):
          bufferSize = 0
          for br in self.blockRenderers:
              bufferSize += br.bufferSize()
              if self.renderer.alpha != 0xff:
                  br.setAlpha(self.renderer.alpha)
          self.bufferSize = bufferSize
          self.invalidLayers = set()
          self.needsRedisplay = True
          self.renderer.invalidateMasterList()
  
      needsRedisplay = False
  
      @property
      def done(self):
          return len(self.invalidLayers) == 0
  
  _XYZ = numpy.s_[..., 0:3]
  _ST = numpy.s_[..., 3:5]
  _XYZST = numpy.s_[..., :5]
  _RGBA = numpy.s_[..., 20:24]
  _RGB = numpy.s_[..., 20:23]
  _A = numpy.s_[..., 23]
  
  
  def makeVertexTemplates(xmin=0, ymin=0, zmin=0, xmax=1, ymax=1, zmax=1):
          return numpy.array([
  
               # FaceXIncreasing:
                                [[xmax, ymin, zmax, (zmin * 16), 16 - (ymin * 16), 0x0b],
                                 [xmax, ymin, zmin, (zmax * 16), 16 - (ymin * 16), 0x0b],
                                 [xmax, ymax, zmin, (zmax * 16), 16 - (ymax * 16), 0x0b],
                                 [xmax, ymax, zmax, (zmin * 16), 16 - (ymax * 16), 0x0b],
                                 ],
  
               # FaceXDecreasing:
                                [[xmin, ymin, zmin, (zmin * 16), 16 - (ymin * 16), 0x0b],
                                 [xmin, ymin, zmax, (zmax * 16), 16 - (ymin * 16), 0x0b],
                                 [xmin, ymax, zmax, (zmax * 16), 16 - (ymax * 16), 0x0b],
                                 [xmin, ymax, zmin, (zmin * 16), 16 - (ymax * 16), 0x0b]],
  
  
               # FaceYIncreasing:
                                [[xmin, ymax, zmin, xmin * 16, 16 - (zmax * 16), 0x11],  # ne
                                 [xmin, ymax, zmax, xmin * 16, 16 - (zmin * 16), 0x11],  # nw
                                 [xmax, ymax, zmax, xmax * 16, 16 - (zmin * 16), 0x11],  # sw
                                 [xmax, ymax, zmin, xmax * 16, 16 - (zmax * 16), 0x11]],  # se
  
               # FaceYDecreasing:
                                [[xmin, ymin, zmin, xmin * 16, 16 - (zmax * 16), 0x08],
                                 [xmax, ymin, zmin, xmax * 16, 16 - (zmax * 16), 0x08],
                                 [xmax, ymin, zmax, xmax * 16, 16 - (zmin * 16), 0x08],
                                 [xmin, ymin, zmax, xmin * 16, 16 - (zmin * 16), 0x08]],
  
               # FaceZIncreasing:
                                [[xmin, ymin, zmax, xmin * 16, 16 - (ymin * 16), 0x0d],
                                 [xmax, ymin, zmax, xmax * 16, 16 - (ymin * 16), 0x0d],
                                 [xmax, ymax, zmax, xmax * 16, 16 - (ymax * 16), 0x0d],
                                 [xmin, ymax, zmax, xmin * 16, 16 - (ymax * 16), 0x0d]],
  
               # FaceZDecreasing:
                                [[xmax, ymin, zmin, xmin * 16, 16 - (ymin * 16), 0x0d],
                                 [xmin, ymin, zmin, xmax * 16, 16 - (ymin * 16), 0x0d],
                                 [xmin, ymax, zmin, xmax * 16, 16 - (ymax * 16), 0x0d],
                                 [xmax, ymax, zmin, xmin * 16, 16 - (ymax * 16), 0x0d],
                                ],
  
          ])
  
  elementByteLength = 24
  
  
  def createPrecomputedVertices():
      height = 16
      precomputedVertices = [numpy.zeros(shape=(16, 16, height, 4, 6),  # x,y,z,s,t,rg, ba
                                    dtype='float32') for d in faceVertexTemplates]
  
      xArray = numpy.arange(16)[:, numpy.newaxis, numpy.newaxis, numpy.newaxis]
      zArray = numpy.arange(16)[numpy.newaxis, :, numpy.newaxis, numpy.newaxis]
      yArray = numpy.arange(height)[numpy.newaxis, numpy.newaxis, :, numpy.newaxis]
  
      for dir in range(len(faceVertexTemplates)):
          precomputedVertices[dir][_XYZ][..., 0] = xArray
          precomputedVertices[dir][_XYZ][..., 1] = yArray
          precomputedVertices[dir][_XYZ][..., 2] = zArray
          precomputedVertices[dir][_XYZ] += faceVertexTemplates[dir][..., 0:3]  # xyz
  
          precomputedVertices[dir][_ST] = faceVertexTemplates[dir][..., 3:5]  # s
          precomputedVertices[dir].view('uint8')[_RGB] = faceVertexTemplates[dir][..., 5, numpy.newaxis]
          precomputedVertices[dir].view('uint8')[_A] = 0xff
  
      return precomputedVertices
  
  faceVertexTemplates = makeVertexTemplates()
  
  
  class ChunkCalculator (object):
      cachedTemplate = None
      cachedTemplateHeight = 0
  
      whiteLight = numpy.array([[[15] * 16] * 16] * 16, numpy.uint8)
      precomputedVertices = createPrecomputedVertices()
  
      def __init__(self, level):
          self.makeRenderstates(level.materials)
  
              # del xArray, zArray, yArray
          self.nullVertices = numpy.zeros((0,) * len(self.precomputedVertices[0].shape), dtype=self.precomputedVertices[0].dtype)
          from leveleditor import Settings
  
          Settings.fastLeaves.addObserver(self)
          Settings.roughGraphics.addObserver(self)
  
      class renderstatePlain(object):
          @classmethod
          def bind(self):
              pass
  
          @classmethod
          def release(self):
              pass
  
      class renderstateVines(object):
          @classmethod
          def bind(self):
              GL.glDisable(GL.GL_CULL_FACE)
              GL.glEnable(GL.GL_ALPHA_TEST)
  
          @classmethod
          def release(self):
              GL.glEnable(GL.GL_CULL_FACE)
              GL.glDisable(GL.GL_ALPHA_TEST)
  
      class renderstateLowDetail(object):
          @classmethod
          def bind(self):
              GL.glDisable(GL.GL_CULL_FACE)
              GL.glDisable(GL.GL_TEXTURE_2D)
  
          @classmethod
          def release(self):
              GL.glEnable(GL.GL_CULL_FACE)
              GL.glEnable(GL.GL_TEXTURE_2D)
  
      class renderstateAlphaTest(object):
          @classmethod
          def bind(self):
              GL.glEnable(GL.GL_ALPHA_TEST)
  
          @classmethod
          def release(self):
              GL.glDisable(GL.GL_ALPHA_TEST)
  
      class _renderstateAlphaBlend(object):
          @classmethod
          def bind(self):
              GL.glEnable(GL.GL_BLEND)
  
          @classmethod
          def release(self):
              GL.glDisable(GL.GL_BLEND)
  
      class renderstateWater(_renderstateAlphaBlend):
          pass
  
      class renderstateIce(_renderstateAlphaBlend):
          pass
  
      class renderstateEntity(object):
          @classmethod
          def bind(self):
              GL.glDisable(GL.GL_DEPTH_TEST)
              # GL.glDisable(GL.GL_CULL_FACE)
              GL.glDisable(GL.GL_TEXTURE_2D)
              GL.glEnable(GL.GL_BLEND)
  
          @classmethod
          def release(self):
              GL.glEnable(GL.GL_DEPTH_TEST)
              # GL.glEnable(GL.GL_CULL_FACE)
              GL.glEnable(GL.GL_TEXTURE_2D)
              GL.glDisable(GL.GL_BLEND)
  
      renderstates = (
          renderstatePlain,
          renderstateVines,
          renderstateLowDetail,
          renderstateAlphaTest,
          renderstateIce,
          renderstateWater,
          renderstateEntity,
      )
  
      def makeRenderstates(self, materials):
          self.blockRendererClasses = [
              GenericBlockRenderer,
              LeafBlockRenderer,
              PlantBlockRenderer,
              TorchBlockRenderer,
              WaterBlockRenderer,
              SlabBlockRenderer,
              PlateBlockRenderer,
          ]
          if materials.name in ("Alpha", "Pocket"):
              self.blockRendererClasses += [
                  RailBlockRenderer,
                  LadderBlockRenderer,
                  SnowBlockRenderer,
                  CarpetBlockRenderer,
                  CactusBlockRenderer,
                  PaneBlockRenderer,
                  CakeBlockRenderer,
                  DaylightBlockRenderer,
                  #LeverBlockRenderer,
                  BedBlockRenderer,
                  EnchantingBlockRenderer,
                  RedstoneBlockRenderer,
                  IceBlockRenderer,
                  #ButtonBlockRenderer,
                  FenceBlockRenderer,
                  NetherFenceBlockRenderer,
                  StairBlockRenderer,
                  RepeaterBlockRenderer,
                  VineBlockRenderer,
                  PlateBlockRenderer,
              # button, floor plate, door -> 1-cube features
              # lever, sign, wall sign, stairs -> 2-cube features
  
              # repeater
              # fence
  
              # bed
              # cake
              # portal
              ]
  
          self.materialMap = materialMap = numpy.zeros((pymclevel.materials.id_limit,), 'uint8')
          materialMap[1:] = 1  # generic blocks
  
          materialCount = 2
  
          for br in self.blockRendererClasses[1:]:  # skip generic blocks
              materialMap[br.getBlocktypes(materials)] = materialCount
              br.materialIndex = materialCount
              materialCount += 1
  
          self.exposedMaterialMap = numpy.array(materialMap)
          self.addTransparentMaterials(self.exposedMaterialMap, materialCount)
  
      def addTransparentMaterials(self, mats, materialCount):
          transparentMaterials = [
              pymclevel.materials.alphaMaterials.Glass,
              pymclevel.materials.alphaMaterials.GlassPane,
              pymclevel.materials.alphaMaterials.IronBars,
              pymclevel.materials.alphaMaterials.MonsterSpawner,
              pymclevel.materials.alphaMaterials.Vines,
              pymclevel.materials.alphaMaterials.Fire,
              pymclevel.materials.alphaMaterials.Trapdoor,
              pymclevel.materials.alphaMaterials.Lever,
              pymclevel.materials.alphaMaterials.BrewingStand,
  
          ]
          for b in transparentMaterials:
              mats[b.ID] = materialCount
              materialCount += 1
  
      hiddenOreMaterials = numpy.arange(pymclevel.materials.id_limit, dtype='uint8')
      hiddenOreMaterials[2] = 1  # don't show boundaries between dirt,grass,sand,gravel,stone
      hiddenOreMaterials[3] = 1
      hiddenOreMaterials[12] = 1
      hiddenOreMaterials[13] = 1
  
      roughMaterials = numpy.ones((pymclevel.materials.id_limit,), dtype='uint8')
      roughMaterials[0] = 0
      addTransparentMaterials(None, roughMaterials, 2)
  
      def calcFacesForChunkRenderer(self, cr):
          if 0 == len(cr.invalidLayers):
  #            layers = set(br.layer for br in cr.blockRenderers)
  #            assert set() == cr.visibleLayers.difference(layers)
  
              return
  
          lod = cr.detailLevel
          cx, cz = cr.chunkPosition
          level = cr.renderer.level
          try:
              chunk = level.getChunk(cx, cz)
          except Exception, e:
              logging.warn(u"Error reading chunk: %s", e)
              yield
              return
  
          yield
          brs = []
          classes = [
              TileEntityRenderer,
              MonsterRenderer,
              ItemRenderer,
              TileTicksRenderer,
              TerrainPopulatedRenderer,
              LowDetailBlockRenderer,
              OverheadBlockRenderer,
          ]
          existingBlockRenderers = dict(((type(b), b) for b in cr.blockRenderers))
  
          for blockRendererClass in classes:
              if cr.detailLevel not in blockRendererClass.detailLevels:
                  continue
              if blockRendererClass.layer not in cr.visibleLayers:
                  continue
              if blockRendererClass.layer not in cr.invalidLayers:
                  if blockRendererClass in existingBlockRenderers:
                      brs.append(existingBlockRenderers[blockRendererClass])
  
                  continue
  
              br = blockRendererClass(self)
              br.detailLevel = cr.detailLevel
  
              for _ in br.makeChunkVertices(chunk):
                  yield
              brs.append(br)
  
          blockRenderers = []
  
          # Recalculate high detail blocks if needed, otherwise retain the high detail renderers
          if lod == 0 and Layer.Blocks in cr.invalidLayers:
              for _ in self.calcHighDetailFaces(cr, blockRenderers):
                  yield
          else:
              blockRenderers.extend(br for br in cr.blockRenderers if type(br) not in classes)
  
          # Add the layer renderers
          blockRenderers.extend(brs)
          cr.blockRenderers = blockRenderers
  
          cr.vertexArraysDone()
          raise StopIteration
  
      def getNeighboringChunks(self, chunk):
          cx, cz = chunk.chunkPosition
          level = chunk.world
  
          neighboringChunks = {}
          for dir, dx, dz in ((pymclevel.faces.FaceXDecreasing, -1, 0),
                             (pymclevel.faces.FaceXIncreasing, 1, 0),
                             (pymclevel.faces.FaceZDecreasing, 0, -1),
                             (pymclevel.faces.FaceZIncreasing, 0, 1)):
              if not level.containsChunk(cx + dx, cz + dz):
                  neighboringChunks[dir] = pymclevel.infiniteworld.ZeroChunk(level.Height)
              else:
                  # if not level.chunkIsLoaded(cx+dx,cz+dz):
                  #    raise StopIteration
                  try:
                      neighboringChunks[dir] = level.getChunk(cx + dx, cz + dz)
                  except (EnvironmentError, pymclevel.mclevelbase.ChunkNotPresent, pymclevel.mclevelbase.ChunkMalformed):
                      neighboringChunks[dir] = pymclevel.infiniteworld.ZeroChunk(level.Height)
          return neighboringChunks
  
      def getAreaBlocks(self, chunk, neighboringChunks):
          chunkWidth, chunkLength, chunkHeight = chunk.Blocks.shape
  
          areaBlocks = numpy.zeros((chunkWidth + 2, chunkLength + 2, chunkHeight + 2), numpy.uint16)
          areaBlocks[1:-1, 1:-1, 1:-1] = chunk.Blocks
          areaBlocks[:1, 1:-1, 1:-1] = neighboringChunks[pymclevel.faces.FaceXDecreasing].Blocks[-1:, :chunkLength, :chunkHeight]
          areaBlocks[-1:, 1:-1, 1:-1] = neighboringChunks[pymclevel.faces.FaceXIncreasing].Blocks[:1, :chunkLength, :chunkHeight]
          areaBlocks[1:-1, :1, 1:-1] = neighboringChunks[pymclevel.faces.FaceZDecreasing].Blocks[:chunkWidth, -1:, :chunkHeight]
          areaBlocks[1:-1, -1:, 1:-1] = neighboringChunks[pymclevel.faces.FaceZIncreasing].Blocks[:chunkWidth, :1, :chunkHeight]
          return areaBlocks
  
      def getFacingBlockIndices(self, areaBlocks, areaBlockMats):
          facingBlockIndices = [None] * 6
  
          exposedFacesX = (areaBlockMats[:-1, 1:-1, 1:-1] != areaBlockMats[1:, 1:-1, 1:-1])
  
          facingBlockIndices[pymclevel.faces.FaceXDecreasing] = exposedFacesX[:-1]
          facingBlockIndices[pymclevel.faces.FaceXIncreasing] = exposedFacesX[1:]
  
          exposedFacesZ = (areaBlockMats[1:-1, :-1, 1:-1] != areaBlockMats[1:-1, 1:, 1:-1])
  
          facingBlockIndices[pymclevel.faces.FaceZDecreasing] = exposedFacesZ[:, :-1]
          facingBlockIndices[pymclevel.faces.FaceZIncreasing] = exposedFacesZ[:, 1:]
  
          exposedFacesY = (areaBlockMats[1:-1, 1:-1, :-1] != areaBlockMats[1:-1, 1:-1, 1:])
  
          facingBlockIndices[pymclevel.faces.FaceYDecreasing] = exposedFacesY[:, :, :-1]
          facingBlockIndices[pymclevel.faces.FaceYIncreasing] = exposedFacesY[:, :, 1:]
          return facingBlockIndices
  
      def getAreaBlockLights(self, chunk, neighboringChunks):
          chunkWidth, chunkLength, chunkHeight = chunk.Blocks.shape
          lights = chunk.BlockLight
          skyLight = chunk.SkyLight
          finalLight = self.whiteLight
  
          if lights != None:
              finalLight = lights
          if skyLight != None:
              finalLight = numpy.maximum(skyLight, lights)
  
          areaBlockLights = numpy.ones((chunkWidth + 2, chunkLength + 2, chunkHeight + 2), numpy.uint8)
          areaBlockLights[:] = 15
  
          areaBlockLights[1:-1, 1:-1, 1:-1] = finalLight
  
          nc = neighboringChunks[pymclevel.faces.FaceXDecreasing]
          numpy.maximum(nc.SkyLight[-1:, :chunkLength, :chunkHeight],
                  nc.BlockLight[-1:, :chunkLength, :chunkHeight],
                  areaBlockLights[0:1, 1:-1, 1:-1])
  
          nc = neighboringChunks[pymclevel.faces.FaceXIncreasing]
          numpy.maximum(nc.SkyLight[:1, :chunkLength, :chunkHeight],
                  nc.BlockLight[:1, :chunkLength, :chunkHeight],
                  areaBlockLights[-1:, 1:-1, 1:-1])
  
          nc = neighboringChunks[pymclevel.faces.FaceZDecreasing]
          numpy.maximum(nc.SkyLight[:chunkWidth, -1:, :chunkHeight],
                  nc.BlockLight[:chunkWidth, -1:, :chunkHeight],
                  areaBlockLights[1:-1, 0:1, 1:-1])
  
          nc = neighboringChunks[pymclevel.faces.FaceZIncreasing]
          numpy.maximum(nc.SkyLight[:chunkWidth, :1, :chunkHeight],
                  nc.BlockLight[:chunkWidth, :1, :chunkHeight],
                  areaBlockLights[1:-1, -1:, 1:-1])
  
          minimumLight = 4
          # areaBlockLights[areaBlockLights<minimumLight]=minimumLight
          numpy.clip(areaBlockLights, minimumLight, 16, areaBlockLights)
  
          return areaBlockLights
  
      def calcHighDetailFaces(self, cr, blockRenderers):  # ForChunk(self, chunkPosition = (0,0), level = None, alpha = 1.0):
          """ calculate the geometry for a chunk renderer from its blockMats, data,
          and lighting array. fills in the cr's blockRenderers with verts
          for each block facing and material"""
  
          # chunkBlocks and chunkLights shall be indexed [x,z,y] to follow infdev's convention
          cx, cz = cr.chunkPosition
          level = cr.renderer.level
  
          chunk = level.getChunk(cx, cz)
          neighboringChunks = self.getNeighboringChunks(chunk)
  
          areaBlocks = self.getAreaBlocks(chunk, neighboringChunks)
          yield
  
          areaBlockLights = self.getAreaBlockLights(chunk, neighboringChunks)
          yield
  
          slabs = areaBlocks == pymclevel.materials.alphaMaterials.StoneSlab.ID  #If someone could combine these, that would be great.
          if slabs.any():
              areaBlockLights[slabs] = areaBlockLights[:, :, 1:][slabs[:, :, :-1]]
          yield
          
          woodSlabs = areaBlocks == pymclevel.materials.alphaMaterials.OakWoodSlab.ID
          if woodSlabs.any():
              areaBlockLights[woodSlabs] = areaBlockLights[:, :, 1:][woodSlabs[:, :, :-1]]
          yield
  
          redSlabs = areaBlocks == pymclevel.materials.alphaMaterials.RedSandstoneSlab.ID
          if redSlabs.any():
              areaBlockLights[redSlabs] = areaBlockLights[:, :, 1:][redSlabs[:, :, :-1]]
          yield
  
  
          showHiddenOres = cr.renderer.showHiddenOres
          if showHiddenOres:
              facingMats = self.hiddenOreMaterials[areaBlocks]
          else:
              facingMats = self.exposedMaterialMap[areaBlocks]
  
          yield
  
          if self.roughGraphics:
              areaBlockMats = self.roughMaterials[areaBlocks]
          else:
              areaBlockMats = self.materialMap[areaBlocks]
  
          facingBlockIndices = self.getFacingBlockIndices(areaBlocks, facingMats)
          yield
  
          for i in self.computeGeometry(chunk, areaBlockMats, facingBlockIndices, areaBlockLights, cr, blockRenderers):
              yield
  
      def computeGeometry(self, chunk, areaBlockMats, facingBlockIndices, areaBlockLights, chunkRenderer, blockRenderers):
          blocks, blockData = chunk.Blocks, chunk.Data
          blockData = blockData & 0xf
          blockMaterials = areaBlockMats[1:-1, 1:-1, 1:-1]
          if self.roughGraphics:
              blockMaterials.clip(0, 1, blockMaterials)
  
          sx = sz = slice(0, 16)
          asx = asz = slice(0, 18)
  
          for y in range(0, chunk.world.Height, 16):
              sy = slice(y, y + 16)
              asy = slice(y, y + 18)
  
              for _i in self.computeCubeGeometry(
                      y,
                      blockRenderers,
                      blocks[sx, sz, sy],
                      blockData[sx, sz, sy],
                      chunk.materials,
                      blockMaterials[sx, sz, sy],
                      [f[sx, sz, sy] for f in facingBlockIndices],
                      areaBlockLights[asx, asz, asy],
                      chunkRenderer):
                  yield
  
      def computeCubeGeometry(self, y, blockRenderers, blocks, blockData, materials, blockMaterials, facingBlockIndices, areaBlockLights, chunkRenderer):
          materialCounts = numpy.bincount(blockMaterials.ravel())
  
          def texMap(blocks, blockData=0, direction=slice(None)):
              return materials.blockTextures[blocks, blockData, direction]  # xxx slow
  
          for blockRendererClass in self.blockRendererClasses:
              mi = blockRendererClass.materialIndex
              if mi >= len(materialCounts) or materialCounts[mi] == 0:
                  continue
  
              blockRenderer = blockRendererClass(self)
              blockRenderer.y = y
              blockRenderer.materials = materials
              for _ in blockRenderer.makeVertices(facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
                  yield
              blockRenderers.append(blockRenderer)
  
              yield
  
      def makeTemplate(self, direction, blockIndices):
          return self.precomputedVertices[direction][blockIndices]
  
  
  class Layer:
      Blocks = "Blocks"
      Entities = "Entities"
      Monsters = "Monsters"
      Items = "Items"
      TileEntities = "TileEntities"
      TileTicks = "TileTicks"
      TerrainPopulated = "TerrainPopulated"
      AllLayers = (Blocks, Entities, Monsters, Items, TileEntities, TileTicks, TerrainPopulated)
  
  
  class BlockRenderer(object):
      # vertexArrays = None
      detailLevels = (0,)
      layer = Layer.Blocks
      directionOffsets = {
          pymclevel.faces.FaceXDecreasing: numpy.s_[:-2, 1:-1, 1:-1],
          pymclevel.faces.FaceXIncreasing: numpy.s_[2:, 1:-1, 1:-1],
          pymclevel.faces.FaceYDecreasing: numpy.s_[1:-1, 1:-1, :-2],
          pymclevel.faces.FaceYIncreasing: numpy.s_[1:-1, 1:-1, 2:],
          pymclevel.faces.FaceZDecreasing: numpy.s_[1:-1, :-2, 1:-1],
          pymclevel.faces.FaceZIncreasing: numpy.s_[1:-1, 2:, 1:-1],
      }
      renderstate = ChunkCalculator.renderstateAlphaTest
  
      def __init__(self, cc):
          self.makeTemplate = cc.makeTemplate
          self.chunkCalculator = cc
          self.vertexArrays = []
  
          pass
  
      @classmethod
      def getBlocktypes(cls, mats):
          return cls.blocktypes
  
      def setAlpha(self, alpha):
          "alpha is an unsigned byte value"
          for a in self.vertexArrays:
              a.view('uint8')[_RGBA][..., 3] = alpha
  
      def bufferSize(self):
          return sum(a.size for a in self.vertexArrays) * 4
  
      def getMaterialIndices(self, blockMaterials):
          return blockMaterials == self.materialIndex
  
      def makeVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          arrays = []
          materialIndices = self.getMaterialIndices(blockMaterials)
          yield
  
          blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
  
          for (direction, exposedFaceIndices) in enumerate(facingBlockIndices):
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              vertexArray = self.makeFaceVertices(direction, materialIndices, exposedFaceIndices, blocks, blockData, blockLight, facingBlockLight, texMap)
              yield
              if len(vertexArray):
                  arrays.append(vertexArray)
          self.vertexArrays = arrays
  
      def makeArrayList(self, chunkPosition, showRedraw):
          l = gl.glGenLists(1)
          GL.glNewList(l, GL.GL_COMPILE)
          self.drawArrays(chunkPosition, showRedraw)
          GL.glEndList()
          return l
  
      def drawArrays(self, chunkPosition, showRedraw):
          cx, cz = chunkPosition
          y = 0
          if hasattr(self, 'y'):
              y = self.y
          with gl.glPushMatrix(GL.GL_MODELVIEW):
              GL.glTranslate(cx << 4, y, cz << 4)
  
              if showRedraw:
                  GL.glColor(1.0, 0.25, 0.25, 1.0)
  
              self.drawVertices()
  
      def drawVertices(self):
          if self.vertexArrays:
              for buf in self.vertexArrays:
                  self.drawFaceVertices(buf)
  
      def drawFaceVertices(self, buf):
          if 0 == len(buf):
              return
          stride = elementByteLength
  
          GL.glVertexPointer(3, GL.GL_FLOAT, stride, (buf.ravel()))
          GL.glTexCoordPointer(2, GL.GL_FLOAT, stride, (buf.ravel()[3:]))
          GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype=numpy.uint8).ravel()[20:]))
  
          GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
  
  
  class EntityRendererGeneric(BlockRenderer):
      renderstate = ChunkCalculator.renderstateEntity
      detailLevels = (0, 1, 2)
  
      def drawFaceVertices(self, buf):
          if 0 == len(buf):
              return
          stride = elementByteLength
  
          GL.glVertexPointer(3, GL.GL_FLOAT, stride, (buf.ravel()))
          GL.glTexCoordPointer(2, GL.GL_FLOAT, stride, (buf.ravel()[3:]))
          GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype=numpy.uint8).ravel()[20:]))
  
          GL.glDepthMask(False)
  
          GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
  
          GL.glLineWidth(2.0)
          GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
  
          GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
  
          GL.glPolygonOffset(DepthOffset.TerrainWire, DepthOffset.TerrainWire)
          with gl.glEnable(GL.GL_POLYGON_OFFSET_FILL, GL.GL_DEPTH_TEST):
              GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
          GL.glDepthMask(True)
  
      def _computeVertices(self, positions, colors, offset=False, chunkPosition=(0, 0)):
          cx, cz = chunkPosition
          x = cx << 4
          z = cz << 4
  
          vertexArray = numpy.zeros(shape=(len(positions), 6, 4, 6), dtype='float32')
          if len(positions):
              positions = numpy.array(positions)
              positions[:, (0, 2)] -= (x, z)
              if offset:
                  positions -= 0.5
  
              vertexArray.view('uint8')[_RGBA] = colors
              vertexArray[_XYZ] = positions[:, numpy.newaxis, numpy.newaxis, :]
              vertexArray[_XYZ] += faceVertexTemplates[_XYZ]
              vertexArray.shape = (len(positions) * 6, 4, 6)
          return vertexArray
  
  
  class TileEntityRenderer(EntityRendererGeneric):
      layer = Layer.TileEntities
  
      def makeChunkVertices(self, chunk):
          tilePositions = []
          for i, ent in enumerate(chunk.TileEntities):
              if i % 10 == 0:
                  yield
              if not 'x' in ent:
                  continue
              tilePositions.append(pymclevel.TileEntity.pos(ent))
          tiles = self._computeVertices(tilePositions, (0xff, 0xff, 0x33, 0x44), chunkPosition=chunk.chunkPosition)
          yield
          self.vertexArrays = [tiles]
  
  
  class BaseEntityRenderer(EntityRendererGeneric):
      pass
  
  
  class MonsterRenderer(BaseEntityRenderer):
      layer = Layer.Entities  # xxx Monsters
      notMonsters = set(["Item", "XPOrb", "Painting"])
  
      def makeChunkVertices(self, chunk):
          monsterPositions = []
          for i, ent in enumerate(chunk.Entities):
              if i % 10 == 0:
                  yield
              id = ent["id"].value
              if id in self.notMonsters:
                  continue
  
              monsterPositions.append(pymclevel.Entity.pos(ent))
  
          monsters = self._computeVertices(monsterPositions,
                                           (0xff, 0x22, 0x22, 0x44),
                                           offset=True,
                                           chunkPosition=chunk.chunkPosition)
          yield
          self.vertexArrays = [monsters]
  
  
  class EntityRenderer(BaseEntityRenderer):
      def makeChunkVertices(self, chunk):
          yield
  #        entityPositions = []
  #        for i, ent in enumerate(chunk.Entities):
  #            if i % 10 == 0:
  #                yield
  #            entityPositions.append(pymclevel.Entity.pos(ent))
  #
  #        entities = self._computeVertices(entityPositions, (0x88, 0x00, 0x00, 0x66), offset=True, chunkPosition=chunk.chunkPosition)
  #        yield
  #        self.vertexArrays = [entities]
  
  
  class ItemRenderer(BaseEntityRenderer):
      layer = Layer.Items
  
      def makeChunkVertices(self, chunk):
          entityPositions = []
          entityColors = []
          colorMap = {
              "Item": (0x22, 0xff, 0x22, 0x5f),
              "XPOrb": (0x88, 0xff, 0x88, 0x5f),
              "Painting": (134, 96, 67, 0x5f),
          }
          for i, ent in enumerate(chunk.Entities):
              if i % 10 == 0:
                  yield
              color = colorMap.get(ent["id"].value)
              if color is None:
                  continue
  
              entityPositions.append(pymclevel.Entity.pos(ent))
              entityColors.append(color)
  
          entities = self._computeVertices(entityPositions, numpy.array(entityColors, dtype='uint8')[:, numpy.newaxis, numpy.newaxis], offset=True, chunkPosition=chunk.chunkPosition)
          yield
          self.vertexArrays = [entities]
  
  
  class TileTicksRenderer(EntityRendererGeneric):
      layer = Layer.TileTicks
  
      def makeChunkVertices(self, chunk):
          if chunk.root_tag and "Level" in chunk.root_tag and "TileTicks" in chunk.root_tag["Level"]:
              ticks = chunk.root_tag["Level"]["TileTicks"]
              if len(ticks):
                  self.vertexArrays.append(self._computeVertices([[t[i].value for i in "xyz"] for t in ticks],
                                                                 (0xff, 0xff, 0xff, 0x44),
                                                                 chunkPosition=chunk.chunkPosition))
  
          yield
  
  
  class TerrainPopulatedRenderer(EntityRendererGeneric):
      layer = Layer.TerrainPopulated
      vertexTemplate = numpy.zeros((6, 4, 6), 'float32')
      vertexTemplate[_XYZ] = faceVertexTemplates[_XYZ]
      vertexTemplate[_XYZ] *= (16, 128, 16)
      color = (255, 200, 155)
      vertexTemplate.view('uint8')[_RGBA] = color + (72,)
  
      def drawFaceVertices(self, buf):
          if 0 == len(buf):
              return
          stride = elementByteLength
  
          GL.glVertexPointer(3, GL.GL_FLOAT, stride, (buf.ravel()))
          GL.glTexCoordPointer(2, GL.GL_FLOAT, stride, (buf.ravel()[3:]))
          GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype=numpy.uint8).ravel()[20:]))
  
          GL.glDepthMask(False)
  
          # GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
          GL.glDisable(GL.GL_CULL_FACE)
  
          with gl.glEnable(GL.GL_DEPTH_TEST):
              GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
  
          GL.glEnable(GL.GL_CULL_FACE)
  
          GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE)
  
          GL.glLineWidth(1.0)
          GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
          GL.glLineWidth(2.0)
          with gl.glEnable(GL.GL_DEPTH_TEST):
              GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
          GL.glLineWidth(1.0)
  
          GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL)
          GL.glDepthMask(True)
  
  #        GL.glPolygonOffset(DepthOffset.TerrainWire, DepthOffset.TerrainWire)
  #        with gl.glEnable(GL.GL_POLYGON_OFFSET_FILL, GL.GL_DEPTH_TEST):
  #            GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
  #
  
      def makeChunkVertices(self, chunk):
          neighbors = self.chunkCalculator.getNeighboringChunks(chunk)
  
          def getpop(ch):
              return getattr(ch, "TerrainPopulated", True)
  
          pop = getpop(chunk)
          yield
          if pop:
              return
  
          visibleFaces = [
              getpop(neighbors[pymclevel.faces.FaceXIncreasing]),
              getpop(neighbors[pymclevel.faces.FaceXDecreasing]),
              True,
              True,
              getpop(neighbors[pymclevel.faces.FaceZIncreasing]),
              getpop(neighbors[pymclevel.faces.FaceZDecreasing]),
          ]
          visibleFaces = numpy.array(visibleFaces, dtype='bool')
          verts = self.vertexTemplate[visibleFaces]
          self.vertexArrays.append(verts)
  
          yield
  
  
  class LowDetailBlockRenderer(BlockRenderer):
      renderstate = ChunkCalculator.renderstateLowDetail
      detailLevels = (1,)
  
      def drawFaceVertices(self, buf):
          if not len(buf):
              return
          stride = 16
  
          GL.glVertexPointer(3, GL.GL_FLOAT, stride, numpy.ravel(buf.ravel()))
          GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, stride, (buf.view(dtype='uint8').ravel()[12:]))
  
          GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
          GL.glDrawArrays(GL.GL_QUADS, 0, len(buf) * 4)
          GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
  
      def setAlpha(self, alpha):
          for va in self.vertexArrays:
              va.view('uint8')[..., -1] = alpha
  
      def makeChunkVertices(self, ch):
          step = 1
  
          level = ch.world
          vertexArrays = []
          blocks = ch.Blocks
          heightMap = ch.HeightMap
  
          heightMap = heightMap[::step, ::step]
          blocks = blocks[::step, ::step]
  
          if 0 in blocks.shape:
              return
  
          chunkWidth, chunkLength, chunkHeight = blocks.shape
          blockIndices = numpy.zeros((chunkWidth, chunkLength, chunkHeight), bool)
  
          gridaxes = list(numpy.indices((chunkWidth, chunkLength)))
          h = numpy.swapaxes(heightMap - 1, 0, 1)[:chunkWidth, :chunkLength]
          numpy.clip(h, 0, chunkHeight - 1, out=h)
  
          gridaxes = [gridaxes[0], gridaxes[1], h]
  
          depths = numpy.zeros((chunkWidth, chunkLength), dtype='uint16')
          depths[1:-1, 1:-1] = reduce(numpy.minimum, (h[1:-1, :-2], h[1:-1, 2:], h[:-2, 1:-1]), h[2:, 1:-1])
          yield
  
          try:
              topBlocks = blocks[gridaxes]
              nonAirBlocks = (topBlocks != 0)
              blockIndices[gridaxes] = nonAirBlocks
              h += 1
              numpy.clip(h, 0, chunkHeight - 1, out=h)
              overblocks = blocks[gridaxes][nonAirBlocks].ravel()
  
          except ValueError, e:
              raise ValueError(str(e.args) + "Chunk shape: {0}".format(blockIndices.shape), sys.exc_info()[-1])
  
          if nonAirBlocks.any():
              blockTypes = blocks[blockIndices]
  
              flatcolors = level.materials.flatColors[blockTypes, ch.Data[blockIndices] & 0xf][:, numpy.newaxis, :]
              # flatcolors[:,:,:3] *= (0.6 + (h * (0.4 / float(chunkHeight-1)))) [topBlocks != 0][:, numpy.newaxis, numpy.newaxis]
              x, z, y = blockIndices.nonzero()
  
              yield
              vertexArray = numpy.zeros((len(x), 4, 4), dtype='float32')
              vertexArray[_XYZ][..., 0] = x[:, numpy.newaxis]
              vertexArray[_XYZ][..., 1] = y[:, numpy.newaxis]
              vertexArray[_XYZ][..., 2] = z[:, numpy.newaxis]
  
              va0 = numpy.array(vertexArray)
  
              va0[..., :3] += faceVertexTemplates[pymclevel.faces.FaceYIncreasing, ..., :3]
  
              overmask = overblocks > 0
              flatcolors[overmask] = level.materials.flatColors[:, 0][overblocks[overmask]][:, numpy.newaxis]
  
              if self.detailLevel == 2:
                  heightfactor = (y / float(2.0 * ch.world.Height)) + 0.5
                  flatcolors[..., :3] *= heightfactor[:, numpy.newaxis, numpy.newaxis]
  
              _RGBA = numpy.s_[..., 12:16]
              va0.view('uint8')[_RGBA] = flatcolors
  
              va0[_XYZ][:, :, 0] *= step
              va0[_XYZ][:, :, 2] *= step
  
              yield
              if self.detailLevel == 2:
                  self.vertexArrays = [va0]
                  return
  
              va1 = numpy.array(vertexArray)
              va1[..., :3] += faceVertexTemplates[pymclevel.faces.FaceXIncreasing, ..., :3]
  
              va1[_XYZ][:, (0, 1), 1] = depths[nonAirBlocks].ravel()[:, numpy.newaxis]  # stretch to floor
              va1[_XYZ][:, (1, 2), 0] -= 1.0  # turn diagonally
              va1[_XYZ][:, (2, 3), 1] -= 0.5  # drop down to prevent intersection pixels
  
              va1[_XYZ][:, :, 0] *= step
              va1[_XYZ][:, :, 2] *= step
  
              flatcolors *= 0.8
  
              va1.view('uint8')[_RGBA] = flatcolors
              grassmask = topBlocks[nonAirBlocks] == 2
              # color grass sides with dirt's color
              va1.view('uint8')[_RGBA][grassmask] = level.materials.flatColors[:, 0][[3]][:, numpy.newaxis]
  
              va2 = numpy.array(va1)
              va2[_XYZ][:, (1, 2), 0] += step
              va2[_XYZ][:, (0, 3), 0] -= step
  
              vertexArrays = [va1, va2, va0]
  
          self.vertexArrays = vertexArrays
  
  
  class OverheadBlockRenderer(LowDetailBlockRenderer):
      detailLevels = (2,)
  
  
  class GenericBlockRenderer(BlockRenderer):
      renderstate = ChunkCalculator.renderstateAlphaTest
  
      materialIndex = 1
  
      def makeGenericVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          vertexArrays = []
          materialIndices = self.getMaterialIndices(blockMaterials)
          yield
  
          for (direction, exposedFaceIndices) in enumerate(facingBlockIndices):
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              blockIndices = materialIndices & exposedFaceIndices
  
              theseBlocks = blocks[blockIndices]
              bdata = blockData[blockIndices]
  
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              def setTexture():
                  vertexArray[_ST] += texMap(theseBlocks, bdata, direction)[:, numpy.newaxis, 0:2]
              setTexture()
  
              def setGrassColors():
                  grass = theseBlocks == pymclevel.materials.alphaMaterials.Grass.ID
                  vertexArray.view('uint8')[_RGB][grass] *= self.grassColor
  
              def getBlockLight():
                  return facingBlockLight[blockIndices]
  
              def setColors():
                  vertexArray.view('uint8')[_RGB] *= getBlockLight()[..., numpy.newaxis, numpy.newaxis]
                  if self.materials.name in ("Alpha", "Pocket"):
                      if direction == pymclevel.faces.FaceYIncreasing:
                          setGrassColors()
              setColors()
              yield
  
              vertexArrays.append(vertexArray)
  
          self.vertexArrays = vertexArrays
  
      grassColor = grassColorDefault = [0.39, 0.77, 0.23]  # 62C743
  
      makeVertices = makeGenericVertices
  
  
  class LeafBlockRenderer(BlockRenderer):
      blocktypes = [18,161]
  
      @property
      def renderstate(self):
          if self.chunkCalculator.fastLeaves:
              return ChunkCalculator.renderstatePlain
          else:
              return ChunkCalculator.renderstateAlphaTest
  
      def makeLeafVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          arrays = []
          materialIndices = self.getMaterialIndices(blockMaterials)
          yield 
  
          if self.materials.name in ("Alpha", "Pocket"):
              if not self.chunkCalculator.fastLeaves:
                  blockIndices = materialIndices
                  data = blockData[blockIndices]
                  data &= 0x3  # ignore decay states
                  leaves = (data == pymclevel.materials.alphaMaterials.Leaves.blockData)
                  pines = (data == pymclevel.materials.alphaMaterials.PineLeaves.blockData)
                  birches = (data == pymclevel.materials.alphaMaterials.BirchLeaves.blockData)
                  jungle = (data == pymclevel.materials.alphaMaterials.JungleLeaves.blockData)
                  acacia = (data == pymclevel.materials.alphaMaterials.AcaciaLeaves.blockData)
                  darkoak = (data == pymclevel.materials.alphaMaterials.DarkOakLeaves.blockData)
                  texes = texMap(blocks[blockIndices], [0], 0)
          else:
              blockIndices = materialIndices
              texes = texMap(blocks[blockIndices], [0], 0)
  
          for (direction, exposedFaceIndices) in enumerate(facingBlockIndices):
              if self.materials.name in ("Alpha", "Pocket"):
                  if self.chunkCalculator.fastLeaves:
                      blockIndices = materialIndices & exposedFaceIndices
                      data = blockData[blockIndices]
                      data &= 0x3  # ignore decay states
                      leaves = (data == pymclevel.materials.alphaMaterials.Leaves.blockData)
                      pines = (data == pymclevel.materials.alphaMaterials.PineLeaves.blockData)
                      birches = (data == pymclevel.materials.alphaMaterials.BirchLeaves.blockData)
                      jungle = (data == pymclevel.materials.alphaMaterials.JungleLeaves.blockData)
                      acacia = (data == pymclevel.materials.alphaMaterials.AcaciaLeaves.blockData)
                      darkoak = (data == pymclevel.materials.alphaMaterials.DarkOakLeaves.blockData)
                      #leaves |= type3
  
                      texes = texMap(blocks[blockIndices], data, 0)
  
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texes[:, numpy.newaxis]
  
              if not self.chunkCalculator.fastLeaves:
                  vertexArray[_ST] -= (0x10, 0x0)
  
              vertexArray.view('uint8')[_RGB] *= facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
              if self.materials.name in ("Alpha", "Pocket"):
                  vertexArray.view('uint8')[_RGB][leaves] *= self.leafColor
                  vertexArray.view('uint8')[_RGB][pines] *= self.pineLeafColor
                  vertexArray.view('uint8')[_RGB][birches] *= self.birchLeafColor
                  vertexArray.view('uint8')[_RGB][jungle] *= self.jungleLeafColor
                  vertexArray.view('uint8')[_RGB][acacia] *= self.acaciaLeafColor
                  vertexArray.view('uint8')[_RGB][darkoak] *= self.darkoakLeafColor
  
              yield
              arrays.append(vertexArray)
  
          self.vertexArrays = arrays
  
      leafColor = leafColorDefault = [0x48 / 255., 0xb5 / 255., 0x18 / 255.]  # 48b518
      pineLeafColor = pineLeafColorDefault = [0x61 / 255., 0x99 / 255., 0x61 / 255.]  # 0x619961
      birchLeafColor = birchLeafColorDefault = [0x80 / 255., 0xa7 / 255., 0x55 / 255.]  # 0x80a755
      jungleLeafColor = jungleLeafColorDefault = [0x48 / 255., 0xb5 / 255., 0x18 / 255.]  # 48b518
      acaciaLeafColor = acaciaLeafColorDefault = [0x48 / 255., 0xb5 / 255., 0x18 / 255.]  # 48b518
      darkoakLeafColor = darkoakLeafColorDefault = [0x48 / 255., 0xb5 / 255., 0x18 / 255.]  # 48b518
  	
  
      makeVertices = makeLeafVertices
  
  
  class PlantBlockRenderer(BlockRenderer):
      @classmethod
      def getBlocktypes(cls, mats):
          # blocktypes = [6, 37, 38, 39, 40, 59, 83]
          # if mats.name != "Classic": blocktypes += [31, 32]  # shrubs, tall grass
          # if mats.name == "Alpha": blocktypes += [115]  # nether wart
          blocktypes = [b.ID for b in mats if b.type in ("DECORATION_CROSS", "NETHER_WART", "CROPS", "STEM")]
  
          return blocktypes
  
      renderstate = ChunkCalculator.renderstateAlphaTest
  
      def makePlantVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          arrays = []
          blockIndices = self.getMaterialIndices(blockMaterials)
          yield
  
          theseBlocks = blocks[blockIndices]
  
          bdata = blockData[blockIndices]
          bdata[theseBlocks == 6] &= 0x5  # xxx saplings only
          texes = texMap(blocks[blockIndices], bdata, 0)
  
          blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
          lights = blockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
  
          colorize = None
          if self.materials.name == "Alpha": #so hacky, someone more competent fix this
              colorize = (theseBlocks == pymclevel.materials.alphaMaterials.TallGrass.ID) & (bdata != 0)
              colorize2 = (theseBlocks == pymclevel.materials.alphaMaterials.TallFlowers.ID) & (bdata != 0) & (bdata !=1) & (bdata !=4) & (bdata !=5)
              
          for direction in (pymclevel.faces.FaceXIncreasing, pymclevel.faces.FaceXDecreasing, pymclevel.faces.FaceZIncreasing, pymclevel.faces.FaceZDecreasing):
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  return
  
              if direction == pymclevel.faces.FaceXIncreasing:
                  vertexArray[_XYZ][..., 1:3, 0] -= 1
              if direction == pymclevel.faces.FaceXDecreasing:
                  vertexArray[_XYZ][..., 1:3, 0] += 1
              if direction == pymclevel.faces.FaceZIncreasing:
                  vertexArray[_XYZ][..., 1:3, 2] -= 1
              if direction == pymclevel.faces.FaceZDecreasing:
                  vertexArray[_XYZ][..., 1:3, 2] += 1
  
              vertexArray[_ST] += texes[:, numpy.newaxis, 0:2]
  
              vertexArray.view('uint8')[_RGB] = 0xf  # ignore precomputed directional light
              vertexArray.view('uint8')[_RGB] *= lights
              if colorize is not None:
                  vertexArray.view('uint8')[_RGB][colorize] *= LeafBlockRenderer.leafColor
                  vertexArray.view('uint8')[_RGB][colorize2] *= LeafBlockRenderer.leafColor
              arrays.append(vertexArray)
              yield
  
          self.vertexArrays = arrays
  
      makeVertices = makePlantVertices
  
  
  class TorchBlockRenderer(BlockRenderer): #Levers here until someone makes a separate renderer for it.
      blocktypes = [pymclevel.materials.alphaMaterials.Torch.ID, 
          pymclevel.materials.alphaMaterials.RedstoneTorchOff.ID,
          pymclevel.materials.alphaMaterials.RedstoneTorchOn.ID,
          pymclevel.materials.alphaMaterials.Lever.ID]
      renderstate = ChunkCalculator.renderstateAlphaTest
      torchOffsetsStraight = [
          [  # FaceXIncreasing
              (-7 / 16., 0, 0),
              (-7 / 16., 0, 0),
              (-7 / 16., 0, 0),
              (-7 / 16., 0, 0),
          ],
          [  # FaceXDecreasing
              (7 / 16., 0, 0),
              (7 / 16., 0, 0),
              (7 / 16., 0, 0),
              (7 / 16., 0, 0),
          ],
          [  # FaceYIncreasing
              (7 / 16., -6 / 16., 7 / 16.),
              (7 / 16., -6 / 16., -7 / 16.),
              (-7 / 16., -6 / 16., -7 / 16.),
              (-7 / 16., -6 / 16., 7 / 16.),
          ],
          [  # FaceYDecreasing
              (7 / 16., 0., 7 / 16.),
              (-7 / 16., 0., 7 / 16.),
              (-7 / 16., 0., -7 / 16.),
              (7 / 16., 0., -7 / 16.),
          ],
  
          [  # FaceZIncreasing
              (0, 0, -7 / 16.),
              (0, 0, -7 / 16.),
              (0, 0, -7 / 16.),
              (0, 0, -7 / 16.)
          ],
          [  # FaceZDecreasing
              (0, 0, 7 / 16.),
              (0, 0, 7 / 16.),
              (0, 0, 7 / 16.),
              (0, 0, 7 / 16.)
          ],
  
      ]
  
      torchOffsetsSouth = [
          [  # FaceXIncreasing
              (-7 / 16., 3 / 16., 0),
              (-7 / 16., 3 / 16., 0),
              (-7 / 16., 3 / 16., 0),
              (-7 / 16., 3 / 16., 0),
          ],
          [  # FaceXDecreasing
              (7 / 16., 3 / 16., 0),
              (7 / 16., 3 / 16., 0),
              (7 / 16., 3 / 16., 0),
              (7 / 16., 3 / 16., 0),
          ],
          [  # FaceYIncreasing
              (7 / 16., -3 / 16., 7 / 16.),
              (7 / 16., -3 / 16., -7 / 16.),
              (-7 / 16., -3 / 16., -7 / 16.),
              (-7 / 16., -3 / 16., 7 / 16.),
          ],
          [  # FaceYDecreasing
              (7 / 16., 3 / 16., 7 / 16.),
              (-7 / 16., 3 / 16., 7 / 16.),
              (-7 / 16., 3 / 16., -7 / 16.),
              (7 / 16., 3 / 16., -7 / 16.),
          ],
  
          [  # FaceZIncreasing
              (0, 3 / 16., -7 / 16.),
              (0, 3 / 16., -7 / 16.),
              (0, 3 / 16., -7 / 16.),
              (0, 3 / 16., -7 / 16.)
          ],
          [  # FaceZDecreasing
              (0, 3 / 16., 7 / 16.),
              (0, 3 / 16., 7 / 16.),
              (0, 3 / 16., 7 / 16.),
              (0, 3 / 16., 7 / 16.),
          ],
  
      ]
      torchOffsetsNorth = torchOffsetsWest = torchOffsetsEast = torchOffsetsSouth
  
      torchOffsets = [
          torchOffsetsStraight,
          torchOffsetsSouth,
          torchOffsetsNorth,
          torchOffsetsWest,
          torchOffsetsEast,
          torchOffsetsStraight,
      ] + [torchOffsetsStraight] * 10
  
      torchOffsets = numpy.array(torchOffsets, dtype='float32')
  
      torchOffsets[1][..., 3, :, 0] -= 0.5
  
      torchOffsets[1][..., 0:2, 0:2, 0] -= 0.5
      torchOffsets[1][..., 4:6, 0:2, 0] -= 0.5
      torchOffsets[1][..., 0:2, 2:4, 0] -= 0.1
      torchOffsets[1][..., 4:6, 2:4, 0] -= 0.1
  
      torchOffsets[1][..., 2, :, 0] -= 0.25
  
      torchOffsets[2][..., 3, :, 0] += 0.5
      torchOffsets[2][..., 0:2, 0:2, 0] += 0.5
      torchOffsets[2][..., 4:6, 0:2, 0] += 0.5
      torchOffsets[2][..., 0:2, 2:4, 0] += 0.1
      torchOffsets[2][..., 4:6, 2:4, 0] += 0.1
      torchOffsets[2][..., 2, :, 0] += 0.25
  
      torchOffsets[3][..., 3, :, 2] -= 0.5
      torchOffsets[3][..., 0:2, 0:2, 2] -= 0.5
      torchOffsets[3][..., 4:6, 0:2, 2] -= 0.5
      torchOffsets[3][..., 0:2, 2:4, 2] -= 0.1
      torchOffsets[3][..., 4:6, 2:4, 2] -= 0.1
      torchOffsets[3][..., 2, :, 2] -= 0.25
  
      torchOffsets[4][..., 3, :, 2] += 0.5
      torchOffsets[4][..., 0:2, 0:2, 2] += 0.5
      torchOffsets[4][..., 4:6, 0:2, 2] += 0.5
      torchOffsets[4][..., 0:2, 2:4, 2] += 0.1
      torchOffsets[4][..., 4:6, 2:4, 2] += 0.1
      torchOffsets[4][..., 2, :, 2] += 0.25
  
      upCoords = ((7, 6), (7, 8), (9, 8), (9, 6))
      downCoords = ((7, 14), (7, 16), (9, 16), (9, 14))
  
      def makeTorchVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          blockIndices = self.getMaterialIndices(blockMaterials)
          torchOffsets = self.torchOffsets[blockData[blockIndices]]
          texes = texMap(blocks[blockIndices], blockData[blockIndices])
          yield
          arrays = []
          for direction in range(6):
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  return
  
              vertexArray.view('uint8')[_RGBA] = 0xff
              vertexArray[_XYZ] += torchOffsets[:, direction]
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_ST] = self.upCoords
              if direction == pymclevel.faces.FaceYDecreasing:
                  vertexArray[_ST] = self.downCoords
              vertexArray[_ST] += texes[:, numpy.newaxis, direction]
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeTorchVertices
  
  
  class RailBlockRenderer(BlockRenderer):
      blocktypes = [pymclevel.materials.alphaMaterials.Rail.ID, pymclevel.materials.alphaMaterials.PoweredRail.ID, pymclevel.materials.alphaMaterials.DetectorRail.ID]
      renderstate = ChunkCalculator.renderstateAlphaTest
  
      railTextures = numpy.array([
          [(0, 128), (0, 144), (16, 144), (16, 128)],  # east-west
          [(0, 128), (16, 128), (16, 144), (0, 144)],  # north-south
          [(0, 128), (16, 128), (16, 144), (0, 144)],  # south-ascending
          [(0, 128), (16, 128), (16, 144), (0, 144)],  # north-ascending
          [(0, 128), (0, 144), (16, 144), (16, 128)],  # east-ascending
          [(0, 128), (0, 144), (16, 144), (16, 128)],  # west-ascending
  
          [(0, 112), (0, 128), (16, 128), (16, 112)],  # northeast corner
          [(0, 128), (16, 128), (16, 112), (0, 112)],  # southeast corner
          [(16, 128), (16, 112), (0, 112), (0, 128)],  # southwest corner
          [(16, 112), (0, 112), (0, 128), (16, 128)],  # northwest corner
  
          [(0, 192), (0, 208), (16, 208), (16, 192)],  # unknown
          [(0, 192), (0, 208), (16, 208), (16, 192)],  # unknown
          [(0, 192), (0, 208), (16, 208), (16, 192)],  # unknown
          [(0, 192), (0, 208), (16, 208), (16, 192)],  # unknown
          [(0, 192), (0, 208), (16, 208), (16, 192)],  # unknown
          [(0, 192), (0, 208), (16, 208), (16, 192)],  # unknown
  
      ], dtype='float32')
      railTextures -= pymclevel.materials.alphaMaterials.blockTextures[pymclevel.materials.alphaMaterials.Rail.ID, 0, 0]
  
      railOffsets = numpy.array([
          [0, 0, 0, 0],
          [0, 0, 0, 0],
  
          [0, 0, 1, 1],  # south-ascending
          [1, 1, 0, 0],  # north-ascending
          [1, 0, 0, 1],  # east-ascending
          [0, 1, 1, 0],  # west-ascending
  
          [0, 0, 0, 0],
          [0, 0, 0, 0],
          [0, 0, 0, 0],
          [0, 0, 0, 0],
  
          [0, 0, 0, 0],
          [0, 0, 0, 0],
          [0, 0, 0, 0],
          [0, 0, 0, 0],
          [0, 0, 0, 0],
          [0, 0, 0, 0],
  
      ], dtype='float32')
  
      def makeRailVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          direction = pymclevel.faces.FaceYIncreasing
          blockIndices = self.getMaterialIndices(blockMaterials)
          yield
  
          bdata = blockData[blockIndices]
          railBlocks = blocks[blockIndices]
          tex = texMap(railBlocks, bdata, pymclevel.faces.FaceYIncreasing)[:, numpy.newaxis, :]
  
          # disable 'powered' or 'pressed' bit for powered and detector rails
          bdata[railBlocks != pymclevel.materials.alphaMaterials.Rail.ID] &= ~0x8
  
          vertexArray = self.makeTemplate(direction, blockIndices)
          if not len(vertexArray):
              return
  
          vertexArray[_ST] = self.railTextures[bdata]
          vertexArray[_ST] += tex
  
          vertexArray[_XYZ][..., 1] -= 0.9
          vertexArray[_XYZ][..., 1] += self.railOffsets[bdata]
  
          blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
  
          vertexArray.view('uint8')[_RGB] *= blockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
          yield
          self.vertexArrays = [vertexArray]
  
      makeVertices = makeRailVertices
  
  
  class LadderBlockRenderer(BlockRenderer):
      blocktypes = [pymclevel.materials.alphaMaterials.Ladder.ID]
  
      ladderOffsets = numpy.array([
          [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)],
          [(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)],
  
          [(0, -1, 0.9), (0, 0, -0.1), (0, 0, -0.1), (0, -1, 0.9)],  # facing east
          [(0, 0, 0.1), (0, -1, -.9), (0, -1, -.9), (0, 0, 0.1)],  # facing west
          [(.9, -1, 0), (.9, -1, 0), (-.1, 0, 0), (-.1, 0, 0)],  # north
          [(0.1, 0, 0), (0.1, 0, 0), (-.9, -1, 0), (-.9, -1, 0)],  # south
  
      ] + [[(0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)]] * 10, dtype='float32')
  
      ladderTextures = numpy.array([
          [(0, 192), (0, 208), (16, 208), (16, 192)],  # unknown
          [(0, 192), (0, 208), (16, 208), (16, 192)],  # unknown
  
          [(64, 96), (64, 80), (48, 80), (48, 96), ],  # e
          [(48, 80), (48, 96), (64, 96), (64, 80), ],  # w
          [(48, 96), (64, 96), (64, 80), (48, 80), ],  # n
          [(64, 80), (48, 80), (48, 96), (64, 96), ],  # s
  
          ] + [[(0, 192), (0, 208), (16, 208), (16, 192)]] * 10, dtype='float32')
  
      def ladderVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          blockIndices = self.getMaterialIndices(blockMaterials)
          blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
          yield
          bdata = blockData[blockIndices]
  
          vertexArray = self.makeTemplate(pymclevel.faces.FaceYIncreasing, blockIndices)
          if not len(vertexArray):
              return
  
          vertexArray[_ST] = self.ladderTextures[bdata]
          vertexArray[_XYZ] += self.ladderOffsets[bdata]
          vertexArray.view('uint8')[_RGB] *= blockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
  
          yield
          self.vertexArrays = [vertexArray]
  
      makeVertices = ladderVertices
  
  class SnowBlockRenderer(BlockRenderer):
      blocktypes = [pymclevel.materials.alphaMaterials.Snow.ID,
                      pymclevel.materials.alphaMaterials.SnowLayer.ID]
  
      def makeSnowVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          #snowIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
  
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
  
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
  
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], 0)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
  
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_XYZ][..., 1] -= 0.875
  
              if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
                  vertexArray[_XYZ][..., 2:4, 1] -= 0.875
                  vertexArray[_ST][..., 2:4, 1] += 14
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeSnowVertices
  
  class CarpetBlockRenderer(BlockRenderer):   #Same as snow renderer, separated for potential snow depth adding later.
      blocktypes = [pymclevel.materials.alphaMaterials.Carpet.ID, pymclevel.materials.alphaMaterials.Lilypad.ID]
  
      def makeCarpetVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
  
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
  
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
  
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_XYZ][..., 1] -= 0.875
  
              if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
                  vertexArray[_XYZ][..., 2:4, 1] -= 0.875
                  vertexArray[_ST][..., 2:4, 1] += 14
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeCarpetVertices
      
  class CactusBlockRenderer(BlockRenderer):  
      blocktypes = [pymclevel.materials.alphaMaterials.Cactus.ID]
  
      def makeCactusVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
  
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
  
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
  
              if direction == pymclevel.faces.FaceXIncreasing:
                  vertexArray[_XYZ][..., 0] -= 0.063
              if direction == pymclevel.faces.FaceXDecreasing:
                  vertexArray[_XYZ][..., 0] += 0.063
              if direction == pymclevel.faces.FaceZIncreasing:
                  vertexArray[_XYZ][..., 2] -= 0.063
              if direction == pymclevel.faces.FaceZDecreasing:
                  vertexArray[_XYZ][..., 2] += 0.063
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeCactusVertices
      
  class PaneBlockRenderer(BlockRenderer):  #Basic no thickness panes, add more faces to widen.
      blocktypes = [pymclevel.materials.alphaMaterials.GlassPane.ID, pymclevel.materials.alphaMaterials.StainedGlassPane.ID, pymclevel.materials.alphaMaterials.IronBars.ID]
  
      def makePaneVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
  
              blockIndices = materialIndices
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
  
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
  
              if direction == pymclevel.faces.FaceXIncreasing:
                  vertexArray[_XYZ][..., 0] -= 0.5
              if direction == pymclevel.faces.FaceXDecreasing:
                  vertexArray[_XYZ][..., 0] += 0.5
              if direction == pymclevel.faces.FaceZIncreasing:
                  vertexArray[_XYZ][..., 2] -= 0.5
              if direction == pymclevel.faces.FaceZDecreasing:
                  vertexArray[_XYZ][..., 2] += 0.5
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makePaneVertices
      
  class PlateBlockRenderer(BlockRenderer): #suggestions to make this the proper shape is appreciated.
      blocktypes = [pymclevel.materials.alphaMaterials.StoneFloorPlate.ID,
                      pymclevel.materials.alphaMaterials.WoodFloorPlate.ID,
                      pymclevel.materials.alphaMaterials.WeightedPressurePlateLight.ID,
                      pymclevel.materials.alphaMaterials.WeightedPressurePlateHeavy.ID]
      def makePlateVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], 0)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_XYZ][..., 1] -= 0.875
              if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
                  vertexArray[_XYZ][..., 2:4, 1] -= 0.875
                  vertexArray[_ST][..., 2:4, 1] += 14
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makePlateVertices
      
  class EnchantingBlockRenderer(BlockRenderer): #Note: Enderportal frame side sprite has been lowered 1 pixel to use this renderer, will need separate renderer for eye.
      blocktypes = [pymclevel.materials.alphaMaterials.EnchantmentTable.ID, pymclevel.materials.alphaMaterials.PortalFrame.ID]
      def makeEnchantingVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
  
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_XYZ][..., 1] -= 0.25
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeEnchantingVertices
      
  class DaylightBlockRenderer(BlockRenderer):
      blocktypes = [pymclevel.materials.alphaMaterials.DaylightSensor.ID, pymclevel.materials.alphaMaterials.DaylightSensorOn.ID]
      def makeDaylightVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
  
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_XYZ][..., 1] -= 0.625
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeDaylightVertices
      
  class BedBlockRenderer(BlockRenderer):
      blocktypes = [pymclevel.materials.alphaMaterials.Bed.ID]
      def makeBedVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
  
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_XYZ][..., 1] -= 0.438
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeBedVertices
  
  class CakeBlockRenderer(BlockRenderer):  #Only shows whole cakes
      blocktypes = [pymclevel.materials.alphaMaterials.Cake.ID]
  
      def makeCakeVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
  
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
  
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
              
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_XYZ][..., 1] -= 0.5
              if direction == pymclevel.faces.FaceXIncreasing:
                  vertexArray[_XYZ][..., 0] -= 0.063
              if direction == pymclevel.faces.FaceXDecreasing:
                  vertexArray[_XYZ][..., 0] += 0.063
              if direction == pymclevel.faces.FaceZIncreasing:
                  vertexArray[_XYZ][..., 2] -= 0.063
              if direction == pymclevel.faces.FaceZDecreasing:
                  vertexArray[_XYZ][..., 2] += 0.063
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeCakeVertices
      
  class RepeaterBlockRenderer(BlockRenderer): #Sticks would be nice
      blocktypes = [pymclevel.materials.alphaMaterials.RedstoneRepeaterOff.ID,
                      pymclevel.materials.alphaMaterials.RedstoneRepeaterOn.ID,
                      pymclevel.materials.alphaMaterials.RedstoneComparatorInactive.ID,
                      pymclevel.materials.alphaMaterials.RedstoneComparatorActive.ID]
      def makeRepeaterVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          materialIndices = self.getMaterialIndices(blockMaterials)
          arrays = []
          yield
          for direction, exposedFaceIndices in enumerate(facingBlockIndices):
              if direction != pymclevel.faces.FaceYIncreasing:
                  blockIndices = materialIndices & exposedFaceIndices
              else:
                  blockIndices = materialIndices
  
              facingBlockLight = areaBlockLights[self.directionOffsets[direction]]
              lights = facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
              vertexArray = self.makeTemplate(direction, blockIndices)
              if not len(vertexArray):
                  continue
  
              vertexArray[_ST] += texMap(blocks[blockIndices], blockData[blockIndices], direction)[:, numpy.newaxis, 0:2]
              vertexArray.view('uint8')[_RGB] *= lights
              if direction == pymclevel.faces.FaceYIncreasing:
                  vertexArray[_XYZ][..., 1] -= 0.875
  
              if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
                  vertexArray[_XYZ][..., 2:4, 1] -= 0.875
                  vertexArray[_ST][..., 2:4, 1] += 14
  
              arrays.append(vertexArray)
              yield
          self.vertexArrays = arrays
  
      makeVertices = makeRepeaterVertices
  
  
  class RedstoneBlockRenderer(BlockRenderer):
      blocktypes = [pymclevel.materials.alphaMaterials.RedstoneWire.ID]
  
      def redstoneVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          blockIndices = self.getMaterialIndices(blockMaterials)
          yield
          vertexArray = self.makeTemplate(pymclevel.faces.FaceYIncreasing, blockIndices)
          if not len(vertexArray):
              return
  
          vertexArray[_ST] += pymclevel.materials.alphaMaterials.blockTextures[55, 0, 0]
          vertexArray[_XYZ][..., 1] -= 0.9
  
          bdata = blockData[blockIndices]
  
          bdata <<= 3
          # bdata &= 0xe0
          bdata[bdata > 0] |= 0x80
  
          vertexArray.view('uint8')[_RGBA][..., 0] = bdata[..., numpy.newaxis]
          vertexArray.view('uint8')[_RGBA][..., 0:3] *= [1, 0, 0]
  
          yield
          self.vertexArrays = [vertexArray]
  
      makeVertices = redstoneVertices
  
  # button, floor plate, door -> 1-cube features
  
  
  #BUTTONS GO HERE
  
  
  class FenceBlockRenderer(BlockRenderer): #This code is written to only accept one texture. Fix me.
      blocktypes = [pymclevel.materials.alphaMaterials.Fence.ID,
                      pymclevel.materials.alphaMaterials.SpruceFence.ID,
                      pymclevel.materials.alphaMaterials.BirchFence.ID,
                      pymclevel.materials.alphaMaterials.JungleFence.ID,
                      pymclevel.materials.alphaMaterials.DarkOakFence.ID,
                      pymclevel.materials.alphaMaterials.AcaciaFence.ID
                      ]
      fenceTemplates = makeVertexTemplates(3 / 8., 0, 3 / 8., 5 / 8., 1, 5 / 8.)
      def fenceVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          fenceMask = self.getMaterialIndices(blockMaterials)
          fenceIndices = fenceMask.nonzero()
          yield
          
          tex = texMap(blocks[fenceMask], blockData[fenceMask], 0)[:, numpy.newaxis, 0:2]
  
          vertexArray = numpy.zeros((len(fenceIndices[0]), 6, 4, 6), dtype='float32')
          for indicies in range(3):
              dimension = (0, 2, 1)[indicies]
  
              vertexArray[..., indicies] = fenceIndices[dimension][:, numpy.newaxis, numpy.newaxis]  # xxx swap z with y using ^
  
          vertexArray[..., 0:5] += self.fenceTemplates[..., 0:5]
          vertexArray[_ST] += pymclevel.materials.alphaMaterials.blockTextures[pymclevel.materials.alphaMaterials.Fence.ID, 0, 0] #Offending line
          
          vertexArray.view('uint8')[_RGB] = self.fenceTemplates[..., 5][..., numpy.newaxis]
          vertexArray.view('uint8')[_A] = 0xFF
          vertexArray.view('uint8')[_RGB] *= areaBlockLights[1:-1, 1:-1, 1:-1][fenceIndices][..., numpy.newaxis, numpy.newaxis, numpy.newaxis]
          vertexArray.shape = (vertexArray.shape[0] * 6, 4, 6)
          yield
          self.vertexArrays = [vertexArray]
  
      makeVertices = fenceVertices
      
  class NetherFenceBlockRenderer(BlockRenderer):
      blocktypes = [pymclevel.materials.alphaMaterials.NetherBrickFence.ID]
      fenceTemplates = makeVertexTemplates(3 / 8., 0, 3 / 8., 5 / 8., 1, 5 / 8.)
      def fenceVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          fenceMask = self.getMaterialIndices(blockMaterials)
          fenceIndices = fenceMask.nonzero()
          yield
  
          vertexArray = numpy.zeros((len(fenceIndices[0]), 6, 4, 6), dtype='float32')
          for indicies in range(3):
              dimension = (0, 2, 1)[indicies]
  
              vertexArray[..., indicies] = fenceIndices[dimension][:, numpy.newaxis, numpy.newaxis] 
  
          vertexArray[..., 0:5] += self.fenceTemplates[..., 0:5]
          vertexArray[_ST] += pymclevel.materials.alphaMaterials.blockTextures[pymclevel.materials.alphaMaterials.NetherBrickFence.ID, 0, 0]
  
          vertexArray.view('uint8')[_RGB] = self.fenceTemplates[..., 5][..., numpy.newaxis]
          vertexArray.view('uint8')[_A] = 0xFF
          vertexArray.view('uint8')[_RGB] *= areaBlockLights[1:-1, 1:-1, 1:-1][fenceIndices][..., numpy.newaxis, numpy.newaxis, numpy.newaxis]
          vertexArray.shape = (vertexArray.shape[0] * 6, 4, 6)
          yield
          self.vertexArrays = [vertexArray]
  
      makeVertices = fenceVertices
  
  
  class StairBlockRenderer(BlockRenderer):
      @classmethod
      def getBlocktypes(cls, mats):
          return [a.ID for a in mats.AllStairs]
  
      # South - FaceXIncreasing
      # North - FaceXDecreasing
      # West - FaceZIncreasing
      # East - FaceZDecreasing
      stairTemplates = numpy.array([makeVertexTemplates(**kw) for kw in [
          # South - FaceXIncreasing
          {"xmin":0.5},
          # North - FaceXDecreasing
          {"xmax":0.5},
          # West - FaceZIncreasing
          {"zmin":0.5},
          # East - FaceZDecreasing
          {"zmax":0.5},
          # Slabtype
          {"ymax":0.5},
          ]
      ])
  
      def stairVertices(self, facingBlockIndices, blocks, blockMaterials, blockData, areaBlockLights, texMap):
          arrays = []
          materialIndices = self.getMaterialIndices(blockMaterials)
          yield
          stairBlocks = blocks[materialIndices]
          stairData = blockData[materialIndices]
          stairTop = (stairData >> 2).astype(bool)
          stairData &= 3
  
          blockLight = areaBlockLights[1:-1, 1:-1, 1:-1]
          x, z, y = materialIndices.nonzero()
  
          for _ in ("slab", "step"):
              vertexArray = numpy.zeros((len(x), 6, 4, 6), dtype='float32')
              for i in range(3):
                  vertexArray[_XYZ][..., i] = (x, y, z)[i][:, numpy.newaxis, numpy.newaxis]
  
              if _ == "step":
                  vertexArray[_XYZST] += self.stairTemplates[4][..., :5]
                  vertexArray[_XYZ][..., 1][stairTop] += 0.5
              else:
                  vertexArray[_XYZST] += self.stairTemplates[stairData][..., :5]
  
              vertexArray[_ST] += texMap(stairBlocks, 0)[..., numpy.newaxis, :]
  
              vertexArray.view('uint8')[_RGB] = self.stairTemplates[4][numpy.newaxis, ..., 5, numpy.newaxis]
              vertexArray.view('uint8')[_RGB] *= 0xf
              vertexArray.view('uint8')[_A] = 0xff
  
              vertexArray.shape = (len(x) * 6, 4, 6)
              yield
              arrays.append(vertexArray)
          self.vertexArrays = arrays
  
      makeVertices = stairVertices
  
  class VineBlockRenderer(BlockRenderer):
      blocktypes = [106]
  
      SouthBit = 1 #FaceZIncreasing
      WestBit = 2 #FaceXDecreasing
      NorthBit = 4 #FaceZDecreasing
      EastBit = 8 #FaceXIncreasing
  
      renderstate = ChunkCalculator.renderstateVines
  
      def vineFaceVertices(self, direction, blockIndices, exposedFaceIndices, blocks, blockData, blockLight, facingBlockLight, texMap):
  
          bdata = blockData[blockIndices]
          blockIndices = numpy.array(blockIndices)
          if direction == pymclevel.faces.FaceZIncreasing:
              blockIndices[blockIndices] = (bdata & 1).astype(bool)
          elif direction == pymclevel.faces.FaceXDecreasing:
              blockIndices[blockIndices] = (bdata & 2).astype(bool)
          elif direction == pymclevel.faces.FaceZDecreasing:
              blockIndices[blockIndices] = (bdata & 4).astype(bool)
          elif direction == pymclevel.faces.FaceXIncreasing:
              blockIndices[blockIndices] = (bdata & 8).astype(bool)
          else:
              return []
          vertexArray = self.makeTemplate(direction, blockIndices)
          if not len(vertexArray):
              return vertexArray
  
          vertexArray[_ST] += texMap(self.blocktypes[0], [0], direction)[:, numpy.newaxis, 0:2]
  
          lights = blockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
          vertexArray.view('uint8')[_RGB] *= lights
  
          vertexArray.view('uint8')[_RGB] *= LeafBlockRenderer.leafColor
  
          if direction == pymclevel.faces.FaceZIncreasing:
              vertexArray[_XYZ][..., 2] -= 0.0625
          if direction == pymclevel.faces.FaceXDecreasing:
              vertexArray[_XYZ][..., 0] += 0.0625
          if direction == pymclevel.faces.FaceZDecreasing:
              vertexArray[_XYZ][..., 2] += 0.0625
          if direction == pymclevel.faces.FaceXIncreasing:
              vertexArray[_XYZ][..., 0] -= 0.0625
  
          return vertexArray
  
      makeFaceVertices = vineFaceVertices
  
  
  class SlabBlockRenderer(BlockRenderer):
      blocktypes = [pymclevel.materials.alphaMaterials.OakWoodSlab.ID, 
                      pymclevel.materials.alphaMaterials.StoneSlab.ID, 
                      pymclevel.materials.alphaMaterials.RedSandstoneSlab.ID]
  
      def slabFaceVertices(self, direction, blockIndices, facingBlockLight, blocks, blockData, blockLight, areaBlockLights, texMap):
  
          lights = areaBlockLights[blockIndices][..., numpy.newaxis, numpy.newaxis]
          bdata = blockData[blockIndices]
          top = (bdata >> 3).astype(bool)
          bdata &= 7
  
          vertexArray = self.makeTemplate(direction, blockIndices)
          if not len(vertexArray):
              return vertexArray
  
          vertexArray[_ST] += texMap(blocks[blockIndices], bdata, direction)[:, numpy.newaxis, 0:2]
          vertexArray.view('uint8')[_RGB] *= lights
  
          if direction == pymclevel.faces.FaceYIncreasing:
              vertexArray[_XYZ][..., 1] -= 0.5
  
          if direction != pymclevel.faces.FaceYIncreasing and direction != pymclevel.faces.FaceYDecreasing:
              vertexArray[_XYZ][..., 2:4, 1] -= 0.5
              vertexArray[_ST][..., 2:4, 1] += 8
  
          vertexArray[_XYZ][..., 1][top] += 0.5
  
          return vertexArray
  
      makeFaceVertices = slabFaceVertices
  
  
  class WaterBlockRenderer(BlockRenderer):
      waterID = 9
      blocktypes = [8, waterID]
      renderstate = ChunkCalculator.renderstateWater
  
      def waterFaceVertices(self, direction, blockIndices, exposedFaceIndices, blocks, blockData, blockLight, facingBlockLight, texMap):
          blockIndices = blockIndices & exposedFaceIndices
          vertexArray = self.makeTemplate(direction, blockIndices)
          vertexArray[_ST] += texMap(self.waterID, 0, 0)[numpy.newaxis, numpy.newaxis]
          vertexArray.view('uint8')[_RGB] *= facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
          return vertexArray
  
      makeFaceVertices = waterFaceVertices
  
  
  class IceBlockRenderer(BlockRenderer):
      iceID = 79
      blocktypes = [iceID]
      renderstate = ChunkCalculator.renderstateIce
  
      def iceFaceVertices(self, direction, blockIndices, exposedFaceIndices, blocks, blockData, blockLight, facingBlockLight, texMap):
          blockIndices = blockIndices & exposedFaceIndices
          vertexArray = self.makeTemplate(direction, blockIndices)
          vertexArray[_ST] += texMap(self.iceID, 0, 0)[numpy.newaxis, numpy.newaxis]
          vertexArray.view('uint8')[_RGB] *= facingBlockLight[blockIndices][..., numpy.newaxis, numpy.newaxis]
          return vertexArray
  
      makeFaceVertices = iceFaceVertices
  
  from glutils import DisplayList
  
  
  class MCRenderer(object):
      isPreviewer = False
  
      def __init__(self, level=None, alpha=1.0):
          self.render = True
          self.origin = (0, 0, 0)
          self.rotation = 0
  
          self.bufferUsage = 0
  
          self.invalidChunkQueue = deque()
          self._chunkWorker = None
          self.chunkRenderers = {}
          self.loadableChunkMarkers = DisplayList()
          self.visibleLayers = set(Layer.AllLayers)
  
          self.masterLists = None
  
          alpha = alpha * 255
          self.alpha = (int(alpha) & 0xff)
  
          self.chunkStartTime = datetime.now()
          self.oldChunkStartTime = self.chunkStartTime
  
          self.oldPosition = None
  
          self.chunkSamples = [timedelta(0, 0, 0)] * 15
  
          self.chunkIterator = None
  
          import leveleditor
          Settings = leveleditor.Settings
  
          Settings.fastLeaves.addObserver(self)
  
          Settings.roughGraphics.addObserver(self)
          Settings.showHiddenOres.addObserver(self)
          Settings.vertexBufferLimit.addObserver(self)
  
          Settings.drawEntities.addObserver(self)
          Settings.drawTileEntities.addObserver(self)
          Settings.drawTileTicks.addObserver(self)
          Settings.drawUnpopulatedChunks.addObserver(self, "drawTerrainPopulated")
          Settings.drawMonsters.addObserver(self)
          Settings.drawItems.addObserver(self)
  
          Settings.showChunkRedraw.addObserver(self, "showRedraw")
          Settings.spaceHeight.addObserver(self)
          Settings.targetFPS.addObserver(self, "targetFPS")
  
          self.level = level
  
      chunkClass = ChunkRenderer
      calculatorClass = ChunkCalculator
  
      minViewDistance = 2
      maxViewDistance = 24
  
      _viewDistance = 8
  
      needsRedraw = True
  
      def toggleLayer(self, val, layer):
          if val:
              self.visibleLayers.add(layer)
          else:
              self.visibleLayers.discard(layer)
          for cr in self.chunkRenderers.itervalues():
              cr.invalidLayers.add(layer)
  
          self.loadNearbyChunks()
  
      def layerProperty(layer, default=True):  # @NoSelf
          attr = intern("_draw" + layer)
  
          def _get(self):
              return getattr(self, attr, default)
  
          def _set(self, val):
              if val != _get(self):
                  setattr(self, attr, val)
                  self.toggleLayer(val, layer)
  
          return property(_get, _set)
  
      drawEntities = layerProperty(Layer.Entities)
      drawTileEntities = layerProperty(Layer.TileEntities)
      drawTileTicks = layerProperty(Layer.TileTicks)
      drawMonsters = layerProperty(Layer.Monsters)
      drawItems = layerProperty(Layer.Items)
      drawTerrainPopulated = layerProperty(Layer.TerrainPopulated)
  
      def inSpace(self):
          if self.level is None:
              return True
          h = self.position[1]
          return ((h > self.level.Height + self.spaceHeight) or
                  (h <= -self.spaceHeight))
  
      def chunkDistance(self, cpos):
          camx, camy, camz = self.position
  
          # if the renderer is offset into the world somewhere, adjust for that
          ox, oy, oz = self.origin
          camx -= ox
          camz -= oz
  
          camcx = int(numpy.floor(camx)) >> 4
          camcz = int(numpy.floor(camz)) >> 4
  
          cx, cz = cpos
  
          return max(abs(cx - camcx), abs(cz - camcz))
  
      overheadMode = False
  
      def detailLevelForChunk(self, cpos):
          if self.overheadMode:
              return 2
          if self.isPreviewer:
              w, l, h = self.level.bounds.size
              if w + l < 256:
                  return 0
  
          distance = self.chunkDistance(cpos) - self.viewDistance
          if distance > 0 or self.inSpace():
              return 1
          return 0
  
      def getViewDistance(self):
          return self._viewDistance
  
      def setViewDistance(self, vd):
          vd = int(vd) & 0xfffe
          vd = min(max(vd, self.minViewDistance), self.maxViewDistance)
          if vd != self._viewDistance:
              self._viewDistance = vd
              self.viewDistanceChanged()
              # self.invalidateChunkMarkers()
  
      viewDistance = property(getViewDistance, setViewDistance, None, "View Distance")
  
      @property
      def effectiveViewDistance(self):
          if self.inSpace():
              return self.viewDistance * 4
          else:
              return self.viewDistance * 2
  
      def viewDistanceChanged(self):
          self.oldPosition = None  # xxx
          self.discardMasterList()
          self.loadNearbyChunks()
          self.discardChunksOutsideViewDistance()
  
      maxWorkFactor = 64
      minWorkFactor = 1
      workFactor = 2
  
      chunkCalculator = None
  
      _level = None
  
      @property
      def level(self):
          return self._level
  
      @level.setter
      def level(self, level):
          """ this probably warrants creating a new renderer """
          self.stopWork()
  
          self._level = level
          self.oldPosition = None
          self.position = (0, 0, 0)
          self.chunkCalculator = None
  
          self.invalidChunkQueue = deque()
  
          self.discardAllChunks()
  
          self.loadableChunkMarkers.invalidate()
  
          if level:
              self.chunkCalculator = self.calculatorClass(self.level)
  
              self.oldPosition = None
              level.allChunks
  
          self.loadNearbyChunks()
  
      position = (0, 0, 0)
  
      def loadChunksStartingFrom(self, wx, wz, distance=None):  # world position
          if None is self.level:
              return
  
          cx = wx >> 4
          cz = wz >> 4
  
          if distance is None:
              d = self.effectiveViewDistance
          else:
              d = distance
  
          self.chunkIterator = self.iterateChunks(wx, wz, d * 2)
  
      def iterateChunks(self, x, z, d):
          cx = x >> 4
          cz = z >> 4
  
          yield (cx, cz)
  
          step = dir = 1
  
          while True:
              for i in range(step):
                  cx += dir
                  yield (cx, cz)
  
              for i in range(step):
                  cz += dir
                  yield (cx, cz)
  
              step += 1
              if step > d and not self.overheadMode:
                  raise StopIteration
  
              dir = -dir
  
      chunkIterator = None
  
      @property
      def chunkWorker(self):
          if self._chunkWorker is None:
              self._chunkWorker = self.makeWorkIterator()
          return self._chunkWorker
  
      def stopWork(self):
          self._chunkWorker = None
  
      def discardAllChunks(self):
          self.bufferUsage = 0
          self.forgetAllDisplayLists()
          self.chunkRenderers = {}
          self.oldPosition = None  # xxx force reload
  
      def discardChunksInBox(self, box):
          self.discardChunks(box.chunkPositions)
  
      def discardChunksOutsideViewDistance(self):
          if self.overheadMode:
              return
  
          # print "discardChunksOutsideViewDistance"
          d = self.effectiveViewDistance
          cx = (self.position[0] - self.origin[0]) / 16
          cz = (self.position[2] - self.origin[2]) / 16
  
          origin = (cx - d, cz - d)
          size = d * 2
  
          if not len(self.chunkRenderers):
              return
          (ox, oz) = origin
          bytes = 0
          # chunks = numpy.fromiter(self.chunkRenderers.iterkeys(), dtype='int32', count=len(self.chunkRenderers))
          chunks = numpy.fromiter(self.chunkRenderers.iterkeys(), dtype='i,i', count=len(self.chunkRenderers))
          chunks.dtype = 'int32'
          chunks.shape = len(self.chunkRenderers), 2
  
          if size:
              outsideChunks = chunks[:, 0] < ox - 1
              outsideChunks |= chunks[:, 0] > ox + size
              outsideChunks |= chunks[:, 1] < oz - 1
              outsideChunks |= chunks[:, 1] > oz + size
              chunks = chunks[outsideChunks]
  
          self.discardChunks(chunks)
  
      def discardChunks(self, chunks):
          for cx, cz in chunks:
              self.discardChunk(cx, cz)
          self.oldPosition = None  # xxx force reload
  
      def discardChunk(self, cx, cz):
          " discards the chunk renderer for this chunk and compresses the chunk "
          if (cx, cz) in self.chunkRenderers:
              self.bufferUsage -= self.chunkRenderers[cx, cz].bufferSize
              self.chunkRenderers[cx, cz].forgetDisplayLists()
              del self.chunkRenderers[cx, cz]
  
      _fastLeaves = False
  
      @property
      def fastLeaves(self):
          return self._fastLeaves
  
      @fastLeaves.setter
      def fastLeaves(self, val):
          if self._fastLeaves != bool(val):
              self.discardAllChunks()
  
          self._fastLeaves = bool(val)
  
      _roughGraphics = False
  
      @property
      def roughGraphics(self):
          return self._roughGraphics
  
      @roughGraphics.setter
      def roughGraphics(self, val):
          if self._roughGraphics != bool(val):
              self.discardAllChunks()
  
          self._roughGraphics = bool(val)
  
      _showHiddenOres = False
  
      @property
      def showHiddenOres(self):
          return self._showHiddenOres
  
      @showHiddenOres.setter
      def showHiddenOres(self, val):
          if self._showHiddenOres != bool(val):
              self.discardAllChunks()
  
          self._showHiddenOres = bool(val)
  
      def invalidateChunk(self, cx, cz, layers=None):
          " marks the chunk for regenerating vertex data and display lists "
          if (cx, cz) in self.chunkRenderers:
              # self.chunkRenderers[(cx,cz)].invalidate()
              # self.bufferUsage -= self.chunkRenderers[(cx, cz)].bufferSize
  
              self.chunkRenderers[(cx, cz)].invalidate(layers)
              # self.bufferUsage += self.chunkRenderers[(cx, cz)].bufferSize
  
              self.invalidChunkQueue.append((cx, cz))  # xxx encapsulate
  
      def invalidateChunksInBox(self, box, layers=None):
          # If the box is at the edge of any chunks, expanding by 1 makes sure the neighboring chunk gets redrawn.
          box = box.expand(1)
  
          self.invalidateChunks(box.chunkPositions, layers)
  
      def invalidateEntitiesInBox(self, box):
          self.invalidateChunks(box.chunkPositions, [Layer.Entities])
  
      def invalidateChunks(self, chunks, layers=None):
          for c in chunks:
              cx, cz = c
              self.invalidateChunk(cx, cz, layers)
  
          self.stopWork()
          self.discardMasterList()
          self.loadNearbyChunks()
  
      def invalidateAllChunks(self, layers=None):
          self.invalidateChunks(self.chunkRenderers.iterkeys(), layers)
  
      def forgetAllDisplayLists(self):
          for cr in self.chunkRenderers.itervalues():
              cr.forgetDisplayLists()
  
      def invalidateMasterList(self):
          self.discardMasterList()
  
      shouldRecreateMasterList = True
  
      def discardMasterList(self):
          self.shouldRecreateMasterList = True
  
      @property
      def shouldDrawAll(self):
          box = self.level.bounds
          return self.isPreviewer and box.width + box.length < 256
  
      distanceToChunkReload = 32.0
  
      def cameraMovedFarEnough(self):
          if self.shouldDrawAll:
              return False
          if self.oldPosition is None:
              return True
  
          cPos = self.position
          oldPos = self.oldPosition
  
          cameraDelta = self.distanceToChunkReload
  
          return any([abs(x - y) > cameraDelta for x, y in zip(cPos, oldPos)])
  
      def loadVisibleChunks(self):
          """ loads nearby chunks if the camera has moved beyond a certain distance """
  
          # print "loadVisibleChunks"
          if self.cameraMovedFarEnough():
              if datetime.now() - self.lastVisibleLoad > timedelta(0, 0.5):
                  self.discardChunksOutsideViewDistance()
                  self.loadNearbyChunks()
  
                  self.oldPosition = self.position
                  self.lastVisibleLoad = datetime.now()
  
      lastVisibleLoad = datetime.now()
  
      def loadNearbyChunks(self):
          if None is self.level:
              return
          # print "loadNearbyChunks"
          cameraPos = self.position
  
          if self.shouldDrawAll:
              self.loadAllChunks()
          else:
              # subtract self.origin to load nearby chunks correctly for preview renderers
              self.loadChunksStartingFrom(int(cameraPos[0]) - self.origin[0], int(cameraPos[2]) - self.origin[2])
  
      def loadAllChunks(self):
          box = self.level.bounds
  
          self.loadChunksStartingFrom(box.origin[0] + box.width / 2, box.origin[2] + box.length / 2, max(box.width, box.length))
  
      _floorTexture = None
  
      @property
      def floorTexture(self):
          if self._floorTexture is None:
              self._floorTexture = Texture(self.makeFloorTex)
          return self._floorTexture
  
      def makeFloorTex(self):
          color0 = (0xff, 0xff, 0xff, 0x22)
          color1 = (0xff, 0xff, 0xff, 0x44)
  
          img = numpy.array([color0, color1, color1, color0], dtype='uint8')
  
          GL.glTexParameter(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST)
          GL.glTexParameter(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST)
  
          GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, 2, 2, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, img)
  
      def invalidateChunkMarkers(self):
          self.loadableChunkMarkers.invalidate()
  
      def _drawLoadableChunkMarkers(self):
          if self.level.chunkCount:
              chunkSet = set(self.level.allChunks)
  
              sizedChunks = chunkMarkers(chunkSet)
  
              GL.glPushAttrib(GL.GL_FOG_BIT)
              GL.glDisable(GL.GL_FOG)
  
              GL.glEnable(GL.GL_BLEND)
              GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)
              GL.glPolygonOffset(DepthOffset.ChunkMarkers, DepthOffset.ChunkMarkers)
              GL.glEnable(GL.GL_DEPTH_TEST)
  
              GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
              GL.glEnable(GL.GL_TEXTURE_2D)
              GL.glColor(1.0, 1.0, 1.0, 1.0)
  
              self.floorTexture.bind()
              # chunkColor = numpy.zeros(shape=(chunks.shape[0], 4, 4), dtype='float32')
  #            chunkColor[:]= (1, 1, 1, 0.15)
  #
  #            cc = numpy.array(chunks[:,0] + chunks[:,1], dtype='int32')
  #            cc &= 1
  #            coloredChunks = cc > 0
  #            chunkColor[coloredChunks] = (1, 1, 1, 0.28)
  #            chunkColor *= 255
  #            chunkColor = numpy.array(chunkColor, dtype='uint8')
  #
              # GL.glColorPointer(4, GL.GL_UNSIGNED_BYTE, 0, chunkColor)
              for size, chunks in sizedChunks.iteritems():
                  if not len(chunks):
                      continue
                  chunks = numpy.array(chunks, dtype='float32')
  
                  chunkPosition = numpy.zeros(shape=(chunks.shape[0], 4, 3), dtype='float32')
                  chunkPosition[:, :, (0, 2)] = numpy.array(((0, 0), (0, 1), (1, 1), (1, 0)), dtype='float32')
                  chunkPosition[:, :, (0, 2)] *= size
                  chunkPosition[:, :, (0, 2)] += chunks[:, numpy.newaxis, :]
                  chunkPosition *= 16
                  GL.glVertexPointer(3, GL.GL_FLOAT, 0, chunkPosition.ravel())
                  # chunkPosition *= 8
                  GL.glTexCoordPointer(2, GL.GL_FLOAT, 0, (chunkPosition[..., (0, 2)] * 8).ravel())
                  GL.glDrawArrays(GL.GL_QUADS, 0, len(chunkPosition) * 4)
  
              GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
              GL.glDisable(GL.GL_TEXTURE_2D)
              GL.glDisable(GL.GL_BLEND)
              GL.glDisable(GL.GL_DEPTH_TEST)
              GL.glDisable(GL.GL_POLYGON_OFFSET_FILL)
              GL.glPopAttrib()
  
      def drawLoadableChunkMarkers(self):
          if not self.isPreviewer or isinstance(self.level, pymclevel.MCInfdevOldLevel):
              self.loadableChunkMarkers.call(self._drawLoadableChunkMarkers)
  
          # self.drawCompressedChunkMarkers()
  
      needsImmediateRedraw = False
      viewingFrustum = None
      if "-debuglists" in sys.argv:
          def createMasterLists(self):
              pass
  
          def callMasterLists(self):
              for cr in self.chunkRenderers.itervalues():
                  cr.debugDraw()
      else:
          def createMasterLists(self):
              if self.shouldRecreateMasterList:
                  lists = {}
                  chunkLists = defaultdict(list)
                  chunksPerFrame = 80
                  shouldRecreateAgain = False
  
                  for ch in self.chunkRenderers.itervalues():
                      if chunksPerFrame:
                          if ch.needsRedisplay:
                              chunksPerFrame -= 1
                          ch.makeDisplayLists()
                      else:
                          shouldRecreateAgain = True
  
                      if ch.renderstateLists:
                          for rs in ch.renderstateLists:
                              chunkLists[rs] += ch.renderstateLists[rs]
  
                  for rs in chunkLists:
                      if len(chunkLists[rs]):
                          lists[rs] = numpy.array(chunkLists[rs], dtype='uint32').ravel()
  
                  # lists = lists[lists.nonzero()]
                  self.masterLists = lists
                  self.shouldRecreateMasterList = shouldRecreateAgain
                  self.needsImmediateRedraw = shouldRecreateAgain
  
          def callMasterLists(self):
              for renderstate in self.chunkCalculator.renderstates:
                  if renderstate not in self.masterLists:
                      continue
  
                  if self.alpha != 0xff and renderstate is not ChunkCalculator.renderstateLowDetail:
                      GL.glEnable(GL.GL_BLEND)
                  renderstate.bind()
  
                  GL.glCallLists(self.masterLists[renderstate])
  
                  renderstate.release()
                  if self.alpha != 0xff and renderstate is not ChunkCalculator.renderstateLowDetail:
                      GL.glDisable(GL.GL_BLEND)
  
      errorLimit = 10
  
      def draw(self):
          self.needsRedraw = False
          if not self.level:
              return
          if not self.chunkCalculator:
              return
          if not self.render:
              return
  
          chunksDrawn = 0
          if self.level.materials.name in ("Pocket", "Alpha"):
              GL.glMatrixMode(GL.GL_TEXTURE)
              GL.glScalef(1/2., 1/2., 1/2.)
  
          with gl.glPushMatrix(GL.GL_MODELVIEW):
              dx, dy, dz = self.origin
              GL.glTranslate(dx, dy, dz)
  
              GL.glEnable(GL.GL_CULL_FACE)
              GL.glEnable(GL.GL_DEPTH_TEST)
  
              self.level.materials.terrainTexture.bind()
              GL.glEnable(GL.GL_TEXTURE_2D)
              GL.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY)
  
              offset = DepthOffset.PreviewRenderer if self.isPreviewer else DepthOffset.Renderer
              GL.glPolygonOffset(offset, offset)
              GL.glEnable(GL.GL_POLYGON_OFFSET_FILL)
  
              self.createMasterLists()
              try:
                  self.callMasterLists()
  
              except GL.GLError, e:
                  if self.errorLimit:
                      self.errorLimit -= 1
                      traceback.print_exc()
                      print e
  
              GL.glDisable(GL.GL_POLYGON_OFFSET_FILL)
  
              GL.glDisable(GL.GL_CULL_FACE)
              GL.glDisable(GL.GL_DEPTH_TEST)
  
              GL.glDisable(GL.GL_TEXTURE_2D)
              GL.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY)
                  # if self.drawLighting:
              self.drawLoadableChunkMarkers()
  
          if self.level.materials.name in ("Pocket", "Alpha"):
              GL.glMatrixMode(GL.GL_TEXTURE)
              GL.glScalef(2., 2., 2.)
  
      renderErrorHandled = False
  
      def addDebugInfo(self, addDebugString):
          addDebugString("BU: {0} MB, ".format(
              self.bufferUsage / 1000000,
               ))
  
          addDebugString("WQ: {0}, ".format(len(self.invalidChunkQueue)))
          if self.chunkIterator:
              addDebugString("[LR], ")
  
          addDebugString("CR: {0}, ".format(len(self.chunkRenderers),))
  
      def next(self):
          self.chunkWorker.next()
  
      def makeWorkIterator(self):
          ''' does chunk face and vertex calculation work. returns a generator that can be
          iterated over for smaller work units.'''
  
          try:
              while True:
                  if self.level is None:
                      raise StopIteration
  
                  if len(self.invalidChunkQueue) > 1024:
                      self.invalidChunkQueue.clear()
  
                  if len(self.invalidChunkQueue):
                      c = self.invalidChunkQueue[0]
                      for i in self.workOnChunk(c):
                          yield
                      self.invalidChunkQueue.popleft()
  
                  elif self.chunkIterator is None:
                      raise StopIteration
  
                  else:
                      c = self.chunkIterator.next()
                      if self.vertexBufferLimit:
                          while self.bufferUsage > (0.9 * (self.vertexBufferLimit << 20)):
                              deadChunk = None
                              deadDistance = self.chunkDistance(c)
                              for cr in self.chunkRenderers.itervalues():
                                  dist = self.chunkDistance(cr.chunkPosition)
                                  if dist > deadDistance:
                                      deadChunk = cr
                                      deadDistance = dist
  
                              if deadChunk is not None:
                                  self.discardChunk(*deadChunk.chunkPosition)
  
                              else:
                                  break
  
                          else:
                              for i in self.workOnChunk(c):
                                  yield
  
                      else:
                          for i in self.workOnChunk(c):
                              yield
  
                  yield
  
          finally:
              self._chunkWorker = None
              if self.chunkIterator:
                  self.chunkIterator = None
  
      vertexBufferLimit = 384
  
      def getChunkRenderer(self, c):
          if not (c in self.chunkRenderers):
              cr = self.chunkClass(self, c)
          else:
              cr = self.chunkRenderers[c]
  
          return cr
  
      def calcFacesForChunkRenderer(self, cr):
          self.bufferUsage -= cr.bufferSize
  
          calc = cr.calcFaces()
          work = 0
          for i in calc:
              yield
              work += 1
  
          self.chunkDone(cr, work)
  
      def workOnChunk(self, c):
          work = 0
  
          if self.level.containsChunk(*c):
              cr = self.getChunkRenderer(c)
              if self.viewingFrustum:
                  # if not self.viewingFrustum.visible(numpy.array([[c[0] * 16 + 8, 64, c[1] * 16 + 8, 1.0]]), 64).any():
                  if not self.viewingFrustum.visible1([c[0] * 16 + 8, self.level.Height / 2, c[1] * 16 + 8, 1.0], self.level.Height / 2):
                      raise StopIteration
                      yield
  
              faceInfoCalculator = self.calcFacesForChunkRenderer(cr)
              try:
                  for result in faceInfoCalculator:
                      work += 1
                      if (work % MCRenderer.workFactor) == 0:
                          yield
  
                  self.invalidateMasterList()
  
              except Exception, e:
                  traceback.print_exc()
                  fn = c
  
                  logging.info(u"Skipped chunk {f}: {e}".format(e=e, f=fn))
  
      redrawChunks = 0
  
      def chunkDone(self, chunkRenderer, work):
          self.chunkRenderers[chunkRenderer.chunkPosition] = chunkRenderer
          self.bufferUsage += chunkRenderer.bufferSize
          # print "Chunk {0} used {1} work units".format(chunkRenderer.chunkPosition, work)
          if not self.needsRedraw:
              if self.redrawChunks:
                  self.redrawChunks -= 1
                  if not self.redrawChunks:
                      self.needsRedraw = True
  
              else:
                  self.redrawChunks = 2
  
          if work > 0:
              self.oldChunkStartTime = self.chunkStartTime
              self.chunkStartTime = datetime.now()
              self.chunkSamples.pop(0)
              self.chunkSamples.append(self.chunkStartTime - self.oldChunkStartTime)
  
              cx, cz = chunkRenderer.chunkPosition
  
  
  
  class PreviewRenderer(MCRenderer):
      isPreviewer = True
  
  
  def rendermain():
      renderer = MCRenderer()
  
      renderer.level = pymclevel.mclevel.loadWorld("World1")
      renderer.viewDistance = 6
      renderer.detailLevelForChunk = lambda * x: 0
      start = datetime.now()
  
      renderer.loadVisibleChunks()
  
      try:
          while True:
          # for i in range(100):
              renderer.next()
      except StopIteration:
          pass
      except Exception, e:
          traceback.print_exc()
          print repr(e)
  
      duration = datetime.now() - start
      perchunk = duration / len(renderer.chunkRenderers)
      print "Duration: {0} ({1} chunks per second, {2} per chunk, {3} chunks)".format(duration, 1000000.0 / perchunk.microseconds, perchunk, len(renderer.chunkRenderers))
  
      # display.init( (640, 480), OPENGL | DOUBLEBUF )
      from mcedit import GLDisplayContext
      from OpenGL import GLU
      cxt = GLDisplayContext()
      import pygame
  
      # distance = 4000
      GL.glMatrixMode(GL.GL_PROJECTION)
      GL.glLoadIdentity()
      GLU.gluPerspective(35, 640.0 / 480.0, 0.5, 4000.0)
      h = 366
  
      pos = (0, h, 0)
  
      look = (0.0001, h - 1, 0.0001)
      up = (0, 1, 0)
      GL.glMatrixMode(GL.GL_MODELVIEW)
      GL.glLoadIdentity()
  
      GLU.gluLookAt(pos[0], pos[1], pos[2],
                     look[0], look[1], look[2],
                     up[0], up[1], up[2])
  
      GL.glClearColor(0.0, 0.0, 0.0, 1.0)
  
      framestart = datetime.now()
      frames = 200
      for i in range(frames):
          GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
          renderer.draw()
          pygame.display.flip()
  
      delta = datetime.now() - framestart
      seconds = delta.seconds + delta.microseconds / 1000000.0
      print "{0} frames in {1} ({2} per frame, {3} FPS)".format(frames, delta, delta / frames, frames / seconds)
  
      while True:
          evt = pygame.event.poll()
          if evt.type == pygame.MOUSEBUTTONDOWN:
              break
      # time.sleep(3.0)
  
  
  import traceback
  import cProfile
  
  if __name__ == "__main__":
      cProfile.run("rendermain()", "mcedit.profile")
  