def __init__(self, level, geoClipMapper, node=None):
        """
        level starts at 0 in center
        scale is 2**level
        """
        if node:
            NodePath.__init__(self, node)
        else:
            NodePath.__init__(self, "GeoClipLevel_" + str(level))
        self.level = level
        self.geoClipMapper = geoClipMapper

        self.heightTex = loadTex(
            "renderData/textures/grass"
        )  # some texture as place holder before map is made.
        self.setShaderInput("height", self.heightTex)

        scale = 2**(level)
        self.setScale(scale, scale, 1)

        self.lastTile = None

        self.tileScale = geoClipMapper.baseTileScale * scale

        self.makingTile = False

        self.setShaderInput("tileOffset", 0, 0, 0, 0)
        self.setShaderInput("tilePos", 0, 0, 0, 0)
 def __init__(self,level,geoClipMapper,node=None):
     """
     level starts at 0 in center
     scale is 2**level
     """
     if node:
         NodePath.__init__(self,node)
     else:
         NodePath.__init__(self,"GeoClipLevel_"+str(level))
     self.level=level
     self.geoClipMapper=geoClipMapper
     
     self.heightTex=loadTex("renderData/textures/grass") # some texture as place holder before map is made.
     self.setShaderInput("height",self.heightTex)
     
     scale=2**(level)
     self.setScale(scale,scale,1)
     
     self.lastTile=None
     
     self.tileScale=geoClipMapper.baseTileScale*scale
     
     self.makingTile=False
     
     self.setShaderInput("tileOffset",0,0,0,0)
     self.setShaderInput("tilePos",0,0,0,0)
    def __init__(self,path,terrainNode):
        NodePath.__init__(self,path+"_render")
        
        self.heightScale=.125
        
        d=parseFile(path+'/texList.txt')
        
        def getRenderMapType(name):
            return getattr(TextureStage,name)
        
        def getCombineMode(name):
            return getattr(TextureStage,name)
        
        self.mapTexStages={}
        self.specialMaps={}
        for m in d['Special']:
            s=m.split('\t')
            self.specialMaps[s[1]]=s[0]
        
        # terrainNode holds all the terrain tiles
        self.terrainNode=terrainNode
        self.terrainNode.reparentTo(self)
        #self.terrainNode.setShader(loader.loadShader(path+"/render.sha"))
        self.terrainNode.setShaderAuto()
        
        # List on non map texture stages, and their sizes
        # (TexStage,Size)
        self.texList=[]
        
        if "Tex2D" in d:
            sort=0;
            for m in d["Tex2D"]:
                sort+=1
                s=m.split()
                name=s[0]
                texStage=TextureStage(name+'stage'+str(sort))
                texStage.setSort(sort)
                source=s[1]
                
                def setTexModes(modeText):
                    combineMode=[]
                    for t in modeText:
                        if t[:1]=='M':
                            texStage.setMode(getRenderMapType(t))
                        elif t[:1]=='C':
                            combineMode.append(getCombineMode(t))
                        elif t=='Save':
                            texStage.setSavedResult(True)
                        else:
                            print "Illegal mode info for "+name
                    if len(combineMode)>0:
                        texStage.setCombineRgb(*combineMode)
                    if len(modeText)==0:
                        texStage.setMode(TextureStage.MModulate)
                
                if source=='file':
                    
                    setTexModes(s[3:])
                    
                    self.terrainNode.setTexture(texStage,loadTex(path+"/textures/"+name))
                    self.terrainNode.setShaderInput('tex2D_'+name,loadTex(path+"/textures/"+name))
                    self.texList.append((texStage,float(s[2])))
                    
                elif source=='map':
                    setTexModes(s[2:])
                    self.mapTexStages[s[0]]=texStage

                else:
                    print 'Invalid source for '+name+' int Tex2D'
    def __init__(self, path, tileSource, minScale, focus):
        RenderNode.__init__(self, path, NodePath(path + "_terrainNode"))

        heightMapName = self.specialMaps['height']
        self.heightMapRez = 0
        for s in tileSource.shaders:
            if s.name == heightMapName:
                self.heightMapRez = s.getRez(tileMapSize)
                break

        if self.heightMapRez == 0:
            print 'Failed to determain height map resolution'

        self.setShaderInput("heightMapRez", self.heightMapRez, 0, 0, 0)

        self.focus = focus
        self.minScale = minScale
        self.tileSource = tileSource
        self.heightStage = TextureStage("height")

        rezFactor = 50
        n = rezFactor * 4 - 1

        if n + 4 >= self.heightMapRez:
            print 'Error: Can not have geoClipMap rez higher than height map rez'

        self.rez = n
        m = (n + 1) / 4

        self.baseTileScale = minScale / n * self.heightMapRez
        scale = minScale / (n - 1)
        self.terrainNode.setScale(scale, scale, scale)
        self.shaderHeightScale = self.heightScale / scale
        self.terrainNode.setShaderInput("heightScale", self.shaderHeightScale,
                                        0, 0)
        self.terrainNode.setShader(loader.loadShader("geoClip.sha"))

        def makeGrid(xSize, ySize):
            """ Size is in verts, not squares """

            format = GeomVertexFormat.getV3()
            vdata = GeomVertexData('grid', format, Geom.UHStatic)
            vertex = GeomVertexWriter(vdata, 'vertex')
            grid = Geom(vdata)
            #snode=GeomNode('grid')

            for x in xrange(xSize):
                for y in xrange(ySize):
                    vertex.addData3f(x, y, 0)

            tri = GeomTristrips(Geom.UHStatic)

            def index(lx, ly):
                return ly + lx * (ySize)

            for x in xrange(xSize - 1):
                for y in xrange(ySize):
                    tri.addVertex(index(x, y))
                    tri.addVertex(index(x + 1, y))
                tri.closePrimitive()

            grid.addPrimitive(tri)

            grid.setBoundsType(BoundingVolume.BTBox)
            grid.setBounds(
                BoundingBox(
                    Point3(0, 0, 0),
                    Point3(xSize - 1, ySize - 1, self.shaderHeightScale)))
            #snode.addGeom(grid)
            #snode.setBoundsType(BoundingVolume.BTBox)
            #snode.setBounds(BoundingBox(Point3(0,0,0),Point3(xSize-1,ySize-1,self.shaderHeightScale)))
            #snode.setFinal(True)
            return grid

        nxn = makeGrid(n, n)
        mxm = makeGrid(m, m)
        mx3 = makeGrid(m, 3)
        x3xm = makeGrid(3, m)
        m2x2 = makeGrid(2 * m + 1, 2)

        cNode = GeomNode('center')
        cGeom = nxn.makeCopy()
        cGeom.transformVertices(Mat4.translateMat(-n / 2, -n / 2, 0))
        cNode.addGeom(cGeom)
        cGeom.setBoundsType(BoundingVolume.BTBox)
        cGeom.setBounds(
            BoundingBox(Point3(-n / 2, -n / 2, 0),
                        Point3(n / 2 - 1, n / 2 - 1, self.shaderHeightScale)))
        cNode.setBoundsType(BoundingVolume.BTBox)
        center = _GeoClipLevel(0, self, cNode)

        #NodePath(nxn).instanceTo(center).setPos(-n/2,-n/2,0)
        center.reparentTo(self.terrainNode)

        halfOffset = n / 2

        #ring=NodePath("Ring")
        ring = GeomNode('ring')

        def doCorner(x, y):
            xd = x * n / 2 - (x + 1) * m / 2
            yd = y * n / 2 - (y + 1) * m / 2

            def doGeom(g, x, y):
                cGeom = (g).makeCopy()
                cGeom.transformVertices(Mat4.translateMat(x, y, 0))
                cGeom.setBoundsType(BoundingVolume.BTBox)
                b = g.getBounds()
                p = b.getPoint(7)
                cGeom.setBounds(
                    BoundingBox(
                        Point3(x, y, 0),
                        Point3(p.getX() + x,
                               p.getY() + y, self.shaderHeightScale)))
                ring.addGeom(cGeom)

            doGeom(mxm, xd, yd)
            doGeom(mxm, xd, yd - y * (m - 1))
            doGeom(mxm, xd - x * (m - 1), yd)
            #NodePath(mxm).copyTo(ring).setPos(xd,yd,0)
            #NodePath(mxm).copyTo(ring).setPos(xd,yd-y*(m-1),0)
            #NodePath(mxm).copyTo(ring).setPos(xd-x*(m-1),yd,0)

            if x == -1:
                if y == 1:
                    doGeom(mx3, xd, yd - y * (m + 1))
                    #NodePath(mx3).copyTo(ring).setPos(xd,yd-y*(m+1),0)
                else:
                    xd2 = n / 2 - m
                    doGeom(mx3, xd2, yd + 2 * m - 2)
                    #NodePath(mx3).copyTo(ring).setPos(xd2,yd+2*m-2,0)
            else:
                doGeom(x3xm, xd - x * (m + 1), yd)
                #NodePath(x3xm).copyTo(ring).setPos(xd-x*(m+1),yd,0)

        doCorner(-1, -1)
        doCorner(1, -1)
        doCorner(-1, 1)
        doCorner(1, 1)

        ring.setBoundsType(BoundingVolume.BTBox)

        ringCount = 4

        self.levels = [center]
        for i in xrange(ringCount):
            cNode = GeomNode('ring' + str(i))
            cNode.addGeomsFrom(ring)
            '''for c in ring.getChildren():
                x=c.copyTo(r)
                #v1=Point3()
                #v2=Point3()
                #x.calcTightBounds(v1,v2)
                #v2.setZ(1)
                node=x.node()
                node.setBoundsType(BoundingVolume.BTBox)
                node.setBounds(c.node().getBounds())#(BoundingBox(v1,v2))
                node.setFinal(1)
                x.showBounds()'''
            #r.showBounds()
            r = _GeoClipLevel(i + 1, self, cNode)
            r.reparentTo(self.terrainNode)
            r.node().setBoundsType(BoundingVolume.BTBox)
            #r.showBounds()
            self.levels.append(r)

        self.terrainNode.setShaderInput("n", n, 0, 0, 0)
        # Add a task to keep updating the terrain
        taskMgr.add(self.update, "update")

        self.grass = self.setUpGrass(center, n)
        grassTex = loadTex("grassSheet", True)
        self.grass.setShaderInput("grassSheet", grassTex)
        grassTex.setWrapU(Texture.WMClamp)
        grassTex.setWrapV(Texture.WMClamp)

        self.terrainNode.setShaderInput("offset", 0, 0, 0, 0)

        #for r in self.levels:
        #    for node in r.getChildren():
        #        node.setShaderInput("offset",node.getX()+halfOffset,node.getY()+halfOffset,0,0)

        self.centerTile = None
    def __init__(self,path,tileSource,minScale,focus):
        RenderNode.__init__(self,path,NodePath(path+"_terrainNode"))
        
        heightMapName=self.specialMaps['height']
        self.heightMapRez=0
        for s in tileSource.shaders:
            if s.name==heightMapName:
                self.heightMapRez=s.getRez(tileMapSize)
                break
        
        if self.heightMapRez==0: print 'Failed to determain height map resolution'
        
        self.setShaderInput("heightMapRez",self.heightMapRez,0,0,0)
        
        self.focus=focus
        self.minScale=minScale
        self.tileSource=tileSource
        self.heightStage=TextureStage("height")
        
        rezFactor=50
        n=rezFactor*4-1
        
        if n+4>=self.heightMapRez:
            print 'Error: Can not have geoClipMap rez higher than height map rez'
        
        self.rez=n
        m=(n+1)/4
        
        self.baseTileScale=minScale/n*self.heightMapRez
        scale=minScale/(n-1)
        self.terrainNode.setScale(scale,scale,scale)
        self.shaderHeightScale=self.heightScale/scale
        self.terrainNode.setShaderInput("heightScale",self.shaderHeightScale,0,0)
        self.terrainNode.setShader(loader.loadShader("geoClip.sha"))
        
        def makeGrid(xSize,ySize):
            """ Size is in verts, not squares """
            
            format=GeomVertexFormat.getV3()
            vdata=GeomVertexData('grid', format, Geom.UHStatic)
            vertex=GeomVertexWriter(vdata, 'vertex')
            grid=Geom(vdata)
            #snode=GeomNode('grid')
            
            for x in xrange(xSize):
                for y in xrange(ySize):
                    vertex.addData3f(x,y,0)
            
            tri=GeomTristrips(Geom.UHStatic)
            def index(lx,ly):
                return ly+lx*(ySize)
                
            for x in xrange(xSize-1):
                for y in xrange(ySize):
                    tri.addVertex(index(x,y))
                    tri.addVertex(index(x+1,y))
                tri.closePrimitive()
        
            grid.addPrimitive(tri)
            
            grid.setBoundsType(BoundingVolume.BTBox)
            grid.setBounds(BoundingBox(Point3(0,0,0),Point3(xSize-1,ySize-1,self.shaderHeightScale)))
            #snode.addGeom(grid)
            #snode.setBoundsType(BoundingVolume.BTBox)
            #snode.setBounds(BoundingBox(Point3(0,0,0),Point3(xSize-1,ySize-1,self.shaderHeightScale)))
            #snode.setFinal(True)
            return grid
        
        
        
        
        nxn=makeGrid(n,n)
        mxm=makeGrid(m,m)
        mx3=makeGrid(m,3)
        x3xm=makeGrid(3,m)
        m2x2=makeGrid(2*m+1,2)

        cNode=GeomNode('center')
        cGeom=nxn.makeCopy()
        cGeom.transformVertices(Mat4.translateMat(-n/2,-n/2,0))
        cNode.addGeom(cGeom)
        cGeom.setBoundsType(BoundingVolume.BTBox)
        cGeom.setBounds(BoundingBox(Point3(-n/2,-n/2,0),Point3(n/2-1,n/2-1,self.shaderHeightScale)))
        cNode.setBoundsType(BoundingVolume.BTBox)
        center=_GeoClipLevel(0,self,cNode)
        
        
        
        #NodePath(nxn).instanceTo(center).setPos(-n/2,-n/2,0)
        center.reparentTo(self.terrainNode)
        
        halfOffset=n/2
        
        #ring=NodePath("Ring")
        ring=GeomNode('ring')
        def doCorner(x,y):
            xd=x*n/2-(x+1)*m/2
            yd=y*n/2-(y+1)*m/2
            def doGeom(g,x,y):
                cGeom=(g).makeCopy()
                cGeom.transformVertices(Mat4.translateMat(x,y,0))
                cGeom.setBoundsType(BoundingVolume.BTBox)
                b=g.getBounds()
                p=b.getPoint(7)
                cGeom.setBounds(BoundingBox(Point3(x,y,0),Point3(p.getX()+x,p.getY()+y,self.shaderHeightScale)))
                ring.addGeom(cGeom)
            doGeom(mxm,xd,yd)
            doGeom(mxm,xd,yd-y*(m-1))
            doGeom(mxm,xd-x*(m-1),yd)
            #NodePath(mxm).copyTo(ring).setPos(xd,yd,0)
            #NodePath(mxm).copyTo(ring).setPos(xd,yd-y*(m-1),0)
            #NodePath(mxm).copyTo(ring).setPos(xd-x*(m-1),yd,0)
            
            if x==-1:
                if y==1:
                    doGeom(mx3,xd,yd-y*(m+1))
                    #NodePath(mx3).copyTo(ring).setPos(xd,yd-y*(m+1),0)
                else:
                    xd2=n/2-m
                    doGeom(mx3,xd2,yd+2*m-2)
                    #NodePath(mx3).copyTo(ring).setPos(xd2,yd+2*m-2,0)
            else:
                doGeom(x3xm,xd-x*(m+1),yd)
                #NodePath(x3xm).copyTo(ring).setPos(xd-x*(m+1),yd,0)
        
        doCorner(-1,-1)
        doCorner(1,-1)
        doCorner(-1,1)
        doCorner(1,1)
        
        ring.setBoundsType(BoundingVolume.BTBox)
        
        ringCount=4
        
        
        
        self.levels=[center]
        for i in xrange(ringCount):
            cNode=GeomNode('ring'+str(i))
            cNode.addGeomsFrom(ring)
            '''for c in ring.getChildren():
                x=c.copyTo(r)
                #v1=Point3()
                #v2=Point3()
                #x.calcTightBounds(v1,v2)
                #v2.setZ(1)
                node=x.node()
                node.setBoundsType(BoundingVolume.BTBox)
                node.setBounds(c.node().getBounds())#(BoundingBox(v1,v2))
                node.setFinal(1)
                x.showBounds()'''
            #r.showBounds()
            r=_GeoClipLevel(i+1,self,cNode)
            r.reparentTo(self.terrainNode)
            r.node().setBoundsType(BoundingVolume.BTBox)
            #r.showBounds()
            self.levels.append(r)
        
        
        self.terrainNode.setShaderInput("n",n,0,0,0)
        # Add a task to keep updating the terrain
        taskMgr.add(self.update, "update")
        
        
        self.grass=self.setUpGrass(center,n)
        grassTex = loadTex("grassSheet",True)
        self.grass.setShaderInput("grassSheet",grassTex)
        grassTex.setWrapU(Texture.WMClamp)
        grassTex.setWrapV(Texture.WMClamp)
    
        self.terrainNode.setShaderInput("offset",0,0,0,0)
        
        #for r in self.levels:
        #    for node in r.getChildren():
        #        node.setShaderInput("offset",node.getX()+halfOffset,node.getY()+halfOffset,0,0)
        
        self.centerTile=None
    def __init__(self, path, terrainNode):
        NodePath.__init__(self, path + "_render")

        self.heightScale = .125

        d = parseFile(path + '/texList.txt')

        def getRenderMapType(name):
            return getattr(TextureStage, name)

        def getCombineMode(name):
            return getattr(TextureStage, name)

        self.mapTexStages = {}
        self.specialMaps = {}
        for m in d['Special']:
            s = m.split('\t')
            self.specialMaps[s[1]] = s[0]

        # terrainNode holds all the terrain tiles
        self.terrainNode = terrainNode
        self.terrainNode.reparentTo(self)
        #self.terrainNode.setShader(loader.loadShader(path+"/render.sha"))
        self.terrainNode.setShaderAuto()

        # List on non map texture stages, and their sizes
        # (TexStage,Size)
        self.texList = []

        if "Tex2D" in d:
            sort = 0
            for m in d["Tex2D"]:
                sort += 1
                s = m.split()
                name = s[0]
                texStage = TextureStage(name + 'stage' + str(sort))
                texStage.setSort(sort)
                source = s[1]

                def setTexModes(modeText):
                    combineMode = []
                    for t in modeText:
                        if t[:1] == 'M':
                            texStage.setMode(getRenderMapType(t))
                        elif t[:1] == 'C':
                            combineMode.append(getCombineMode(t))
                        elif t == 'Save':
                            texStage.setSavedResult(True)
                        else:
                            print "Illegal mode info for " + name
                    if len(combineMode) > 0:
                        texStage.setCombineRgb(*combineMode)
                    if len(modeText) == 0:
                        texStage.setMode(TextureStage.MModulate)

                if source == 'file':

                    setTexModes(s[3:])

                    self.terrainNode.setTexture(
                        texStage, loadTex(path + "/textures/" + name))
                    self.terrainNode.setShaderInput(
                        'tex2D_' + name, loadTex(path + "/textures/" + name))
                    self.texList.append((texStage, float(s[2])))

                elif source == 'map':
                    setTexModes(s[2:])
                    self.mapTexStages[s[0]] = texStage

                else:
                    print 'Invalid source for ' + name + ' int Tex2D'