def loadFlatQuad(self, fullFilename): cm = CardMaker("cm-%s" % fullFilename) cm.setColor(1.0, 1.0, 1.0, 1.0) aspect = base.camLens.getAspectRatio() htmlWidth = 2.0 * aspect * WEB_WIDTH_PIXELS / float(WIN_WIDTH) htmlHeight = 2.0 * float(WEB_HEIGHT_PIXELS) / float(WIN_HEIGHT) cm.setFrame(-htmlWidth / 2.0, htmlWidth / 2.0, -htmlHeight / 2.0, htmlHeight / 2.0) bottomRightX = WEB_WIDTH_PIXELS / float(WEB_WIDTH + 1) bottomRightY = WEB_HEIGHT_PIXELS / float(WEB_HEIGHT + 1) cm.setUvRange(Point2(0, 1 - bottomRightY), Point2(bottomRightX, 1)) card = cm.generate() quad = NodePath(card) jpgFile = PNMImage(WEB_WIDTH, WEB_HEIGHT) smallerJpgFile = PNMImage() readFile = smallerJpgFile.read(Filename(fullFilename)) if readFile: jpgFile.copySubImage(smallerJpgFile, 0, 0) guiTex = Texture("guiTex") guiTex.setupTexture(Texture.TT2dTexture, WEB_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) guiTex.setMinfilter(Texture.FTLinear) guiTex.load(jpgFile) guiTex.setWrapU(Texture.WMClamp) guiTex.setWrapV(Texture.WMClamp) ts = TextureStage("webTS") quad.setTexture(ts, guiTex) quad.setTransparency(0) quad.setTwoSided(True) quad.setColor(1.0, 1.0, 1.0, 1.0) result = quad else: result = None Texture.setTexturesPower2(1) return result
def loadFlatQuad(self, fullFilename): cm = CardMaker('cm-%s' % fullFilename) cm.setColor(1.0, 1.0, 1.0, 1.0) aspect = base.camLens.getAspectRatio() htmlWidth = 2.0 * aspect * WEB_WIDTH_PIXELS / float(WIN_WIDTH) htmlHeight = 2.0 * float(WEB_HEIGHT_PIXELS) / float(WIN_HEIGHT) cm.setFrame(-htmlWidth / 2.0, htmlWidth / 2.0, -htmlHeight / 2.0, htmlHeight / 2.0) bottomRightX = WEB_WIDTH_PIXELS / float(WEB_WIDTH + 1) bottomRightY = WEB_HEIGHT_PIXELS / float(WEB_HEIGHT + 1) cm.setUvRange(Point2(0, 1 - bottomRightY), Point2(bottomRightX, 1)) card = cm.generate() quad = NodePath(card) jpgFile = PNMImage(WEB_WIDTH, WEB_HEIGHT) smallerJpgFile = PNMImage() readFile = smallerJpgFile.read(Filename(fullFilename)) if readFile: jpgFile.copySubImage(smallerJpgFile, 0, 0) guiTex = Texture('guiTex') guiTex.setupTexture(Texture.TT2dTexture, WEB_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) guiTex.setMinfilter(Texture.FTLinear) guiTex.load(jpgFile) guiTex.setWrapU(Texture.WMClamp) guiTex.setWrapV(Texture.WMClamp) ts = TextureStage('webTS') quad.setTexture(ts, guiTex) quad.setTransparency(0) quad.setTwoSided(True) quad.setColor(1.0, 1.0, 1.0, 1.0) result = quad else: result = None Texture.setTexturesPower2(1) return result
def loadFlatQuad(self, fullFilename): """Load the flat jpg into a quad.""" assert self.notify.debugStateCall(self) #Texture.setTexturesPower2(AutoTextureScale.ATSUp) #Texture.setTexturesPower2(2) cm = CardMaker('cm-%s'%fullFilename) cm.setColor(1.0, 1.0, 1.0, 1.0) aspect = base.camLens.getAspectRatio() htmlWidth = 2.0*aspect * WEB_WIDTH_PIXELS / float(WIN_WIDTH) htmlHeight = 2.0*float(WEB_HEIGHT_PIXELS) / float(WIN_HEIGHT) # the html area will be center aligned and vertically top aligned #cm.setFrame(-htmlWidth/2.0, htmlWidth/2.0, 1.0 - htmlHeight, 1.0) cm.setFrame(-htmlWidth/2.0, htmlWidth/2.0, - htmlHeight / 2.0, htmlHeight / 2.0) bottomRightX = (WEB_WIDTH_PIXELS) / float( WEB_WIDTH +1) bottomRightY = WEB_HEIGHT_PIXELS / float (WEB_HEIGHT+1) #cm.setUvRange(Point2(0,0), Point2(bottomRightX, bottomRightY)) cm.setUvRange(Point2(0,1-bottomRightY), Point2(bottomRightX,1)) card = cm.generate() quad = NodePath(card) #quad.reparentTo(self.parent) jpgFile = PNMImage(WEB_WIDTH, WEB_HEIGHT) smallerJpgFile = PNMImage() readFile = smallerJpgFile.read(Filename(fullFilename)) if readFile: jpgFile.copySubImage(smallerJpgFile, 0, 0) guiTex = Texture("guiTex") guiTex.setupTexture(Texture.TT2dTexture, WEB_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) guiTex.setMinfilter(Texture.FTLinear) guiTex.load(jpgFile) #guiTex.setKeepRamImage(True) #guiTex.makeRamImage() guiTex.setWrapU(Texture.WMClamp) guiTex.setWrapV(Texture.WMClamp) ts = TextureStage('webTS') quad.setTexture(ts, guiTex) #quad.setTexScale(ts, 1.0, -1.0) quad.setTransparency(0) quad.setTwoSided(True) quad.setColor(1.0, 1.0, 1.0, 1.0) result= quad else: # if we have an error loading the file, return None to signify an error result = None #Texture.setTexturesPower2(AutoTextureScale.ATSDown) Texture.setTexturesPower2(1) return result
def make_data(self, hmapfile): # open heightmap for reading pixel data heightmap = PNMImage() heightmap.read(Filename(hmapfile)) xs = heightmap.getXSize() ys = heightmap.getYSize() # generate data bi-dimensional array data = [] for x in range(xs): data.append([]) for y in range(ys): # set data dictionary properties # name name = "cell_" + str(x) + "_" + str(y) # height height = (heightmap.getXel(x, ys - y - 1)[0] * 10) if self.retro == True: if height < 1 : height = height / 5 height = int(height) # c and rgb c = [random.random(), random.random(), random.random()] rgb = (int(c[0] * 255), int(c[1] * 255), int(c[2] * 255)) # default texture texture = self.tiles[0]['tex'] texturenum = 0 score = self.tiles[0]['score'] # from rgb we assign tex and score for n in range(len(self.tiles)): if rgb == self.tiles[n]['rgb']: texture = self.tiles[n]['tex'] texturenum = n score = self.tiles[n]['score'] break # set terrain data dictionary data[x].append({'name':name, 'h':height, 'c':c, 'rgb':rgb, 'tex':texture, 'texnum':texturenum, 'score':score}) return data
def fromImageFile(self, filename, tolerance=2, max_depth=5, debugOutput=False): # the source image img = PNMImage() if not img.read(Filename(filename)): self.log.error('Failed to read %s' % filename) return None
def __init__(self, image_path, rowPerFace, name=None,\ rows=1, cols=1, scale=1.0,\ twoSided=False, alpha=TRANS_ALPHA,\ repeatX=1, repeatY=1,\ anchorX=ALIGN_CENTER, anchorY=ALIGN_BOTTOM): """ Create a card textured with an image. The card is sized so that the ratio between the card and image is the same. """ global SpriteId self.spriteNum = str(SpriteId) SpriteId += 1 scale *= self.PIXEL_SCALE self.animations = {} self.scale = scale self.repeatX = repeatX self.repeatY = repeatY self.flip = {'x': False, 'y': False} self.rows = rows self.cols = cols self.currentFrame = 0 self.currentAnim = None self.loopAnim = False self.frameInterrupt = True # Create the NodePath if name: self.node = NodePath("Sprite2d:%s" % name) else: self.node = NodePath("Sprite2d:%s" % image_path) # Set the attribute for transparency/twosided self.node.node().setAttrib(TransparencyAttrib.make(alpha)) if twoSided: self.node.setTwoSided(True) # Make a filepath self.imgFile = Filename(image_path) if self.imgFile.empty(): raise IOError, "File not found" # Instead of loading it outright, check with the PNMImageHeader if we can open # the file. imgHead = PNMImageHeader() if not imgHead.readHeader(self.imgFile): raise IOError, "PNMImageHeader could not read file. Try using absolute filepaths" # Load the image with a PNMImage image = PNMImage() image.read(self.imgFile) self.sizeX = image.getXSize() self.sizeY = image.getYSize() # We need to find the power of two size for the another PNMImage # so that the texture thats loaded on the geometry won't have artifacts textureSizeX = self.nextsize(self.sizeX) textureSizeY = self.nextsize(self.sizeY) # The actual size of the texture in memory self.realSizeX = textureSizeX self.realSizeY = textureSizeY self.paddedImg = PNMImage(textureSizeX, textureSizeY) if image.hasAlpha(): self.paddedImg.alphaFill(0) # Copy the source image to the image we're actually using self.paddedImg.blendSubImage(image, 0, 0) # We're done with source image, clear it image.clear() # The pixel sizes for each cell self.colSize = self.sizeX / self.cols self.rowSize = self.sizeY / self.rows # How much padding the texture has self.paddingX = textureSizeX - self.sizeX self.paddingY = textureSizeY - self.sizeY # Set UV padding self.uPad = float(self.paddingX) / textureSizeX self.vPad = float(self.paddingY) / textureSizeY # The UV dimensions for each cell self.uSize = (1.0 - self.uPad) / self.cols self.vSize = (1.0 - self.vPad) / self.rows self.cards = [] self.rowPerFace = rowPerFace for i in range(len(rowPerFace)): card = CardMaker("Sprite2d-Geom") # The positions to create the card at if anchorX == self.ALIGN_LEFT: posLeft = 0 posRight = (self.colSize / scale) * repeatX elif anchorX == self.ALIGN_CENTER: posLeft = -(self.colSize / 2.0 / scale) * repeatX posRight = (self.colSize / 2.0 / scale) * repeatX elif anchorX == self.ALIGN_RIGHT: posLeft = -(self.colSize / scale) * repeatX posRight = 0 if anchorY == self.ALIGN_BOTTOM: posTop = 0 posBottom = (self.rowSize / scale) * repeatY elif anchorY == self.ALIGN_CENTER: posTop = -(self.rowSize / 2.0 / scale) * repeatY posBottom = (self.rowSize / 2.0 / scale) * repeatY elif anchorY == self.ALIGN_TOP: posTop = -(self.rowSize / scale) * repeatY posBottom = 0 card.setFrame(posLeft, posRight, posTop, posBottom) card.setHasUvs(True) self.cards.append(self.node.attachNewNode(card.generate())) self.cards[-1].setH(i * 360 / len(rowPerFace)) # Since the texture is padded, we need to set up offsets and scales to make # the texture fit the whole card self.offsetX = (float(self.colSize) / textureSizeX) self.offsetY = (float(self.rowSize) / textureSizeY) # self.node.setTexScale(TextureStage.getDefault(), self.offsetX * repeatX, self.offsetY * repeatY) # self.node.setTexOffset(TextureStage.getDefault(), 0, 1-self.offsetY) self.texture = Texture() self.texture.setXSize(textureSizeX) self.texture.setYSize(textureSizeY) self.texture.setZSize(1) # Load the padded PNMImage to the texture self.texture.load(self.paddedImg) self.texture.setMagfilter(Texture.FTNearest) self.texture.setMinfilter(Texture.FTNearest) #Set up texture clamps according to repeats if repeatX > 1: self.texture.setWrapU(Texture.WMRepeat) else: self.texture.setWrapU(Texture.WMClamp) if repeatY > 1: self.texture.setWrapV(Texture.WMRepeat) else: self.texture.setWrapV(Texture.WMClamp) self.node.setTexture(self.texture) self.setFrame(0)
def __init__(self, image_path, rowPerFace, name=None,\ rows=1, cols=1, scale=1.0,\ twoSided=False, alpha=TRANS_ALPHA,\ repeatX=1, repeatY=1,\ anchorX=ALIGN_CENTER, anchorY=ALIGN_BOTTOM): """ Create a card textured with an image. The card is sized so that the ratio between the card and image is the same. """ global SpriteId self.spriteNum = str(SpriteId) SpriteId += 1 scale *= self.PIXEL_SCALE self.animations = {} self.scale = scale self.repeatX = repeatX self.repeatY = repeatY self.flip = {'x':False,'y':False} self.rows = rows self.cols = cols self.currentFrame = 0 self.currentAnim = None self.loopAnim = False self.frameInterrupt = True # Create the NodePath if name: self.node = NodePath("Sprite2d:%s" % name) else: self.node = NodePath("Sprite2d:%s" % image_path) # Set the attribute for transparency/twosided self.node.node().setAttrib(TransparencyAttrib.make(alpha)) if twoSided: self.node.setTwoSided(True) # Make a filepath self.imgFile = Filename(image_path) if self.imgFile.empty(): raise IOError, "File not found" # Instead of loading it outright, check with the PNMImageHeader if we can open # the file. imgHead = PNMImageHeader() if not imgHead.readHeader(self.imgFile): raise IOError, "PNMImageHeader could not read file. Try using absolute filepaths" # Load the image with a PNMImage image = PNMImage() image.read(self.imgFile) self.sizeX = image.getXSize() self.sizeY = image.getYSize() # We need to find the power of two size for the another PNMImage # so that the texture thats loaded on the geometry won't have artifacts textureSizeX = self.nextsize(self.sizeX) textureSizeY = self.nextsize(self.sizeY) # The actual size of the texture in memory self.realSizeX = textureSizeX self.realSizeY = textureSizeY self.paddedImg = PNMImage(textureSizeX, textureSizeY) if image.hasAlpha(): self.paddedImg.alphaFill(0) # Copy the source image to the image we're actually using self.paddedImg.blendSubImage(image, 0, 0) # We're done with source image, clear it image.clear() # The pixel sizes for each cell self.colSize = self.sizeX/self.cols self.rowSize = self.sizeY/self.rows # How much padding the texture has self.paddingX = textureSizeX - self.sizeX self.paddingY = textureSizeY - self.sizeY # Set UV padding self.uPad = float(self.paddingX)/textureSizeX self.vPad = float(self.paddingY)/textureSizeY # The UV dimensions for each cell self.uSize = (1.0 - self.uPad) / self.cols self.vSize = (1.0 - self.vPad) / self.rows self.cards = [] self.rowPerFace = rowPerFace for i in range(len(rowPerFace)): card = CardMaker("Sprite2d-Geom") # The positions to create the card at if anchorX == self.ALIGN_LEFT: posLeft = 0 posRight = (self.colSize/scale)*repeatX elif anchorX == self.ALIGN_CENTER: posLeft = -(self.colSize/2.0/scale)*repeatX posRight = (self.colSize/2.0/scale)*repeatX elif anchorX == self.ALIGN_RIGHT: posLeft = -(self.colSize/scale)*repeatX posRight = 0 if anchorY == self.ALIGN_BOTTOM: posTop = 0 posBottom = (self.rowSize/scale)*repeatY elif anchorY == self.ALIGN_CENTER: posTop = -(self.rowSize/2.0/scale)*repeatY posBottom = (self.rowSize/2.0/scale)*repeatY elif anchorY == self.ALIGN_TOP: posTop = -(self.rowSize/scale)*repeatY posBottom = 0 card.setFrame(posLeft, posRight, posTop, posBottom) card.setHasUvs(True) self.cards.append(self.node.attachNewNode(card.generate())) self.cards[-1].setH(i * 360/len(rowPerFace)) # Since the texture is padded, we need to set up offsets and scales to make # the texture fit the whole card self.offsetX = (float(self.colSize)/textureSizeX) self.offsetY = (float(self.rowSize)/textureSizeY) # self.node.setTexScale(TextureStage.getDefault(), self.offsetX * repeatX, self.offsetY * repeatY) # self.node.setTexOffset(TextureStage.getDefault(), 0, 1-self.offsetY) self.texture = Texture() self.texture.setXSize(textureSizeX) self.texture.setYSize(textureSizeY) self.texture.setZSize(1) # Load the padded PNMImage to the texture self.texture.load(self.paddedImg) self.texture.setMagfilter(Texture.FTNearest) self.texture.setMinfilter(Texture.FTNearest) #Set up texture clamps according to repeats if repeatX > 1: self.texture.setWrapU(Texture.WMRepeat) else: self.texture.setWrapU(Texture.WMClamp) if repeatY > 1: self.texture.setWrapV(Texture.WMRepeat) else: self.texture.setWrapV(Texture.WMClamp) self.node.setTexture(self.texture) self.setFrame(0)
class Region(DirectObject.DirectObject): '''Stuff''' def __init__(self): self.tiles = [] self.cities = {} self.accept('loadRegion', self.load) self.accept("updatedTiles", self.updateTiles) self.accept("newCity", self.newCity) self.accept("clickForCity", self.checkCity) self.accept("unfoundCity", self.unfoundCity) self.accept("enterCity", self.enterCity) def load(self, container, name="New Region"): '''Loads a new region, usually from connecting to a server Or starting a new or previously saved region. ''' import base64 self.heightmap = PNMImage() imageString = base64.b64decode(container.heightmap) self.heightmap.read(StringStream(imageString)) self.region_size = (self.heightmap.getXSize() - 1, self.heightmap.getYSize() - 1) position = 0 tileid = 0 total_tiles = self.region_size[0] * self.region_size[1] ranges = [] tiles = [] for tile in container.tiles: tiles.append((tile.id, tile.cityid)) for n in range(len(tiles)): try: ranges.append((tiles[n][0], tiles[n + 1][0] - 1, tiles[n][1])) except: ranges.append((tiles[n][0], total_tiles, tiles[n][1])) for r in ranges: for x in range(r[0], r[1] + 1): #print "r0, r1, x", r[0], r[1], x self.tiles.append(Tile(tileid, r[2])) #print "Len", len(self.tiles) tileid += 1 position = 0 for y in range(self.region_size[1]): for x in range(self.region_size[0]): self.tiles[position].coords = (x, y) position += 1 for city in container.cities: self.newCity(city) messenger.send("generateRegion", [self.heightmap, self.tiles, self.cities, container]) def updateTiles(self, container): x = 0 for tile in container: x += 1 self.tiles[tile.id].cityid = tile.cityid print x, "tiles updated from server." messenger.send("updateRegion", [self.heightmap, self.tiles, self.cities]) def newCity(self, city): self.cities[city.id] = { "name": city.name, "mayor": city.mayor, "funds": city.funds, "population": city.population } def checkCity(self, cell): '''Checks for city in given cell for region gui display''' if not cell: return tile = self.getTile(cell[0], cell[1]) if tile.cityid: messenger.send("showRegionCityWindow", [tile.cityid, self.cities[tile.cityid]]) def getTile(self, x, y): '''Returns tile by coordinate. Thankfully smart enough to find a way to not iterate ''' value = y * self.region_size[0] + x return self.tiles[value] def unfoundCity(self, ident): '''Unfounds a city''' del self.cities[ident] def enterCity(self, ident): '''Processess information needed for graphical elements to enter city view.''' # We need to send list of tiles for terrain manager tiles = [] xsum = 0 ysum = 0 n = 0 for tile in self.tiles: if tile.cityid is ident: tiles.append(tile) # We need to compute center of city to target camera there xsum += tile.coords[0] ysum += tile.coords[1] n += 1 xavg = xsum / n yavg = ysum / n position = (xavg, yavg) # We need to send city info so gui elements can be drawn city = self.cities[ident] messenger.send('enterCityView', [ident, city, position, tiles])
class TerrainTile(GeoMipTerrain): """TerrainTiles are the building blocks of a terrain.""" def __init__(self, terrain, x, y): """Builds a Tile for the terrain at input coordinates. Important settings are used directly from the terrain. This allows for easier setting changes, and reduces memory overhead. x and y parameters give the appropriate world coordinates of this tile. """ self.terrain = terrain self.xOffset = x self.yOffset = y self.heightMapDetail = 1 # higher means greater detail self.name = "ID" + str(terrain.id) + "_X" + str(x) + "_Y" + str(y) GeoMipTerrain.__init__(self, name=self.name) self.image = PNMImage() #self.setAutoFlatten(GeoMipTerrain.AFMOff) self.setFocalPoint(self.terrain.focus) self.setAutoFlatten(GeoMipTerrain.AFMOff) self.getRoot().setPos(x, y, 0) if self.terrain.bruteForce: GeoMipTerrain.setBruteforce(self, True) GeoMipTerrain.setBlockSize( self, self.terrain.heightMapSize * self.heightMapDetail) else: GeoMipTerrain.setBlockSize(self, self.terrain.blockSize / 2) #self.setBorderStitching(1) self.setNear(self.terrain.near) self.setFar(self.terrain.far) def update(self): """Updates the GeoMip to use the correct LOD on each block.""" #logging.info("TerrainTile.update()") GeoMipTerrain.update(self) @pstat def updateTask(self, task): """Updates the GeoMip to use the correct LOD on each block.""" self.update() return task.again #@pstat def setHeightField(self, filename): """Set the GeoMip heightfield from a heightmap image.""" GeoMipTerrain.setHeightfield(self, filename) @pstat def generate(self): GeoMipTerrain.generate(self) @pstat def setHeight(self): """Sets the height field to match the height map image.""" self.setHeightField(self.image) @pstat def makeHeightMap(self): """Generate a new heightmap image. Panda3d GeoMipMaps require an image from which to build and update their height field. This function creates the correct image using the tile's position and the Terrain's getHeight() function. """ if SAVED_HEIGHT_MAPS: fileName = "maps/height/" + self.name + ".png" self.getRoot().setTag('EditableTerrain', '1') if self.image.read(Filename(fileName)): logging.info("read heightmap from " + fileName) return heightMapSize = self.terrain.tileSize * self.heightMapDetail + 1 self.image = PNMImage(heightMapSize, heightMapSize, 1, 65535) ySize = self.image.getYSize() - 1 getHeight = self.terrain.getHeight setGray = self.image.setGray xo = self.xOffset yo = self.yOffset d = self.heightMapDetail for x in range(self.image.getXSize()): for y in range(ySize + 1): height = getHeight(x / d + xo, y / d + yo) # feed pixel into image # why is it necessary to invert the y axis I wonder? setGray(x, ySize - y, height) #self.postProcessImage() if SAVED_HEIGHT_MAPS: fileName = "maps/height/" + self.name + ".png" logging.info("saving heightmap to " + fileName) self.image.write(Filename(fileName)) def postProcessImage(self): """Perform filters and manipulations on the heightmap image.""" #self.image.gaussianFilter() def setWireFrame(self, state): self.getRoot().setRenderModeWireframe() def makeSlopeMap(self): self.slopeMap = PNMImage() if SAVED_SLOPE_MAPS: fileName = "maps/slope/" + self.name + ".png" if self.slopeMap.read(Filename(fileName)): logging.info("read slopemap from " + fileName) return self.slopeMap = PNMImage(self.terrain.heightMapSize, self.terrain.heightMapSize) self.slopeMap.makeGrayscale() self.slopeMap.setMaxval(65535) size = self.slopeMap.getYSize() getNormal = self.getNormal setGray = self.slopeMap.setGray for x in range(size): for y in range(size): #note getNormal works at the same resolution as the heightmap normal = getNormal(x, y) # feed pixel into image # why is it necessary to invert the y axis I wonder? #logging.info( normal) normal.z /= self.terrain.getSz() normal.normalize() slope = 1.0 - normal.dot(Vec3(0, 0, 1)) setGray(x, y, slope) if SAVED_SLOPE_MAPS: fileName = "maps/slope/" + self.name + ".png" logging.info("saving slopemap to " + fileName) self.slopeMap.write(Filename(fileName)) def createGroups(self): self.statics = self.getRoot().attachNewNode(self.name + "_statics") self.statics.setSz(1.0 / self.terrain.getSz()) self.statics.setSx(1.0 / self.terrain.getSx()) self.statics.setSy(1.0 / self.terrain.getSy()) self.statics.setShaderAuto() @pstat def make(self): """Build a finished renderable heightMap.""" # apply shader #logging.info( "applying shader") self.terrain.texturer.apply(self.getRoot()) # detail settings #self.getRoot().setSx(1.0 / self.heightMapDetail) #self.getRoot().setSy(1.0 / self.heightMapDetail) #logging.info( "making height map") self.makeHeightMap() #logging.info( "setHeight()") self.setHeight() #self.getRoot().setSz(self.maxHeight) #http://www.panda3d.org/forums/viewtopic.php?t=12054 self.calcAmbientOcclusion() #logging.info( "generate()") self.generate() self.getRoot().setCollideMask(BitMask32.bit(1)) #self.makeSlopeMap() #logging.info( "createGroups()") self.createGroups() self.terrain.populator.populate(self)
class TerrainTile(GeoMipTerrain): """TerrainTiles are the building blocks of a terrain.""" def __init__(self, terrain, x, y): """Builds a Tile for the terrain at input coordinates. Important settings are used directly from the terrain. This allows for easier setting changes and reduces memory overhead. x and y parameters give the appropriate world coordinates of this tile. """ self.terrain = terrain self.xOffset = x self.yOffset = y self.heightMapDetail = 1 # higher means greater detail self.name = "ID" + str(terrain.id) + "_X" + str(x) + "_Y" + str(y) GeoMipTerrain.__init(self, name=self.name) self.image = PNImage() #self.setAutoFlatten(GeoMipTerrain.AFMOff self.setFocalPoint(self.terrain.focus) self.setAutoFlatten(GeoMipTerrain.AFMOff) self.getRoot().setPos(x, y, 0) if self.terrain.bruteForce: GeoMipTerrain.setBruteForce(self, True) GeoMipTerrain.setBlockSize(self, self.terrain.heightMapSize * self.heightMapDetail) else: GeoMipTerrain.setBlockSize(self, self.terrain.blockSize/2) #self.setBorderStitching(1) self.setNear(self.terrain.near) self.setFar(self.terrain.far) def update(self): """Updates the GeoMip to use the correct LOD on each block.""" #logging.info("TerrainTile.update()") GeoMipTerrain.update(self) @pstat def updateTask(self, task): """Updates the GeoMip to use the correct LOD on each block.""" self.update() return task.again #@pstat def setHeightField(self, filename): "Set the GeoMip heightfield from a heightmap image.""" GeoMipTerrain.setHeightfield(self, filename) @pstat def generate(self): GeoMipTerrain.generate(self) @pstat def setHeight(self): """Sets the height field to match the height map image.""" self.setHeightField(self.image) @pstat def makeHeightMap(self): """Generate a new heightmap image. Panda3d GeoMipMaps require an image from which to build and update their height field. This function creates the correct image using the tile's position and the Terrain's getHeight() function. """ if SAVED_HEIGHT_MAPS: fileName = "maps/height/" + self.name + ".png" self.getRoot().setTag('EditableTerrain', '1') if self.image.read(Filename(fileName)): logging.info( "read heightmap from " + fileName) return heightMapSize = self.terrain.tileSize * self.heightMapDetail + 1 self.image = PNMImage(heightMapSize, heightMapSize, 1, 65535) ySize = self.image.getYSize() - 1 getHeight = self.terrain.getHeight setGray = self.image.setGray xo = self.xOffset yo = self.yOffset d = self.heightMapDetail for x in range(self.image.getXSize()): for y in range(ySize + 1): height = getHeight(x / d + xo, y / d + yo) # feed pixel into image # why is it necessary to invert the y axis I wonder? setGray(x, ySize - y, height) #self.postProcessImage() if SAVED_HEIGHT_MAPS: fileName = "maps/height/" + self.name + ".png" logging.info( "saving heightmap to " + fileName) self.image.write(Filename(fileName)) def postProcessImage(self): """Perform filters and manipulations on the heightmap image.""" #self.image.gaussianFilter() def setWireFrame(self, state): self.getRoot().setRenderModeWireframe() def makeSlopeMap(self): self.slopeMap = PNMImage() if SAVED_SLOPE_MAPS: fileName = "maps/slope/" + self.name + ".png" if self.slopeMap.read(Filename(fileName)): logging.info( "read slopemap from " + fileName) return self.slopeMap = PNMImage(self.terrain.heightMapSize, self.terrain.heightMapSize) self.slopeMap.makeGrayscale() self.slopeMap.setMaxval(65535) size = self.slopeMap.getYSize() getNormal = self.getNormal setGray = self.slopeMap.setGray for x in range(size): for y in range(size): #note getNormal works at the same resolution as the heightmap normal = getNormal(x, y) # feed pixel into image # why is it necessary to invert the y axis I wonder? #logging.info( normal) normal.z /= self.terrain.getSz() normal.normalize() slope = 1.0 - normal.dot(Vec3(0, 0, 1)) setGray(x, y, slope) if SAVED_SLOPE_MAPS: fileName = "maps/slope/" + self.name + ".png" logging.info( "saving slopemap to " + fileName) self.slopeMap.write(Filename(fileName)) def createGroups(self): self.statics = self.getRoot().attachNewNode(self.name + "_statics") self.statics.setSz(1.0 / self.terrain.getSz()) self.statics.setSx(1.0 / self.terrain.getSz()) self.statics.setSy(1.0 / self.terrain.getSy()) self.statics.setShaderAuto() @pstat def make(self): """Build a finished renderable heightMap.""" # apply shader #logging.info( "applying shader") self.terrain.texturer.apply(self.getRoot()) # detail settings #self.getRoot().setSz(1.0 / self.heightMapDetail) #self.getRoot().setSy(1.0 / self.heightMapDetail) #logging.info( "making height map") self.makeHeightMap() #logging.info( "setHeight()") self.setHeight() #self.getRoot().setSz(self.maxHeight) #http://www.panda3d.org/forums/viewtopic.php?=t=12054 self.calcAmbientOcclusion() #loggin.info( "generate()") self.generate() self.getRoot().setCollideMask(BitMask32.bit(1)) #self.makeSlopeMap() #logging.info( "createGroups()") self.createGroups() self.terrain.populator.populate(self)
def import_(self, file_name, palettes): from pandac.PandaModules import PNMImage, Filename, VBase4D from pandac.PandaModules import Texture as P3DTexture pnm = PNMImage() pnm.read(Filename.fromOsSpecific(file_name)) tex_pnm = PNMImage(17*256, 1024) tex_pnm.addAlpha() #convert data to same sequence as files texdata = [] for y in range(1024): row = [] for x in range(256): gray = pnm.getXel(x, y) pal_i = int(gray[0] * 15.0) row.append(pal_i) texdata.append(row) #update saving texture testpnm = PNMImage(256, 1024) palette = [(x, x, x, 1) for x in range(16)] colors = [] for color in palette: color_list = [c / 15.0 for c in color[:3]] color_list.append(0 if color == (0, 0, 0, 0) else 1) colors.append(VBase4D(*color_list)) for y in range(1024): row = texdata[y] for x in range(256): testpnm.setXelA(x, y, colors[row[x]]) self.texture2.load(testpnm) self.texture2.setMagfilter(P3DTexture.FTNearest) self.texture2.setMinfilter(P3DTexture.FTLinear) #update texture visible on map self.palettes = [] temp = [] for x in range (16): temp.append((x, x, x, 1)) self.palettes.append(temp) for y, palette in enumerate(palettes): selfpalette = [] for x, color in enumerate(palette.colors.colors): selfpalette.append((color[0],color[1],color[2],1)) self.palettes.append(selfpalette) i = 0 for palette in self.palettes: colors = [] for color in palette: color_list = [c / 15.0 for c in color[:3]] color_list.append(0 if color == (0, 0, 0, 0) else 1) colors.append(VBase4D(*color_list)) for y in range(1024): row = texdata[y] for x in range(256): tex_pnm.setXelA(x + (256*i), y, colors[row[x]]) i += 1 self.texture.load(tex_pnm) self.texture.setMagfilter(P3DTexture.FTNearest) self.texture.setMinfilter(P3DTexture.FTLinear)
class Region(DirectObject.DirectObject): '''Stuff''' def __init__(self): self.tiles = [] self.cities = {} self.accept('loadRegion', self.load) self.accept("updatedTiles", self.updateTiles) self.accept("newCity", self.newCity) self.accept("clickForCity", self.checkCity) self.accept("unfoundCity", self.unfoundCity) self.accept("enterCity", self.enterCity) def load(self, container, name="New Region"): '''Loads a new region, usually from connecting to a server Or starting a new or previously saved region. ''' import base64 self.heightmap = PNMImage() imageString = base64.b64decode(container.heightmap) self.heightmap.read(StringStream(imageString)) self.region_size = (self.heightmap.getXSize()-1, self.heightmap.getYSize()-1) position = 0 tileid = 0 total_tiles = self.region_size[0] * self.region_size[1] ranges = [] tiles = [] for tile in container.tiles: tiles.append((tile.id, tile.cityid)) for n in range(len(tiles)): try: ranges.append((tiles[n][0], tiles[n+1][0]-1, tiles[n][1])) except: ranges.append((tiles[n][0], total_tiles, tiles[n][1])) for r in ranges: for x in range(r[0], r[1]+1): #print "r0, r1, x", r[0], r[1], x self.tiles.append(Tile(tileid, r[2])) #print "Len", len(self.tiles) tileid += 1 position = 0 for y in range(self.region_size[1]): for x in range(self.region_size[0]): self.tiles[position].coords = (x,y) position += 1 for city in container.cities: self.newCity(city) messenger.send("generateRegion", [self.heightmap, self.tiles, self.cities, container]) def updateTiles(self, container): x = 0 for tile in container: x += 1 self.tiles[tile.id].cityid = tile.cityid print x, "tiles updated from server." messenger.send("updateRegion", [self.heightmap, self.tiles, self.cities]) def newCity(self, city): self.cities[city.id] = {"name": city.name, "mayor": city.mayor, "funds": city.funds, "population": city.population} def checkCity(self, cell): '''Checks for city in given cell for region gui display''' if not cell: return tile = self.getTile(cell[0], cell[1]) if tile.cityid: messenger.send("showRegionCityWindow", [tile.cityid, self.cities[tile.cityid]]) def getTile(self, x, y): '''Returns tile by coordinate. Thankfully smart enough to find a way to not iterate ''' value = y * self.region_size[0] + x return self.tiles[value] def unfoundCity(self, ident): '''Unfounds a city''' del self.cities[ident] def enterCity(self, ident): '''Processess information needed for graphical elements to enter city view.''' # We need to send list of tiles for terrain manager tiles = [] xsum = 0 ysum = 0 n = 0 for tile in self.tiles: if tile.cityid is ident: tiles.append(tile) # We need to compute center of city to target camera there xsum += tile.coords[0] ysum += tile.coords[1] n += 1 xavg = xsum/n yavg = ysum/n position = (xavg, yavg) # We need to send city info so gui elements can be drawn city = self.cities[ident] messenger.send('enterCityView', [ident, city, position, tiles])
def _loadInternal(self, resType, filename, locationName=None, preloading=False): ''' Manages the actual loading of a resource given the resource filename and the resource location that contains it. @param resType: A constant that identifies the type of the resource. @param filename: The filename of the resource. @param locationName: The name of the resource location containing the resource. This is optional. @return: A pano.resources.Resource instance if preloading is True, or the actual resource instance if preloading is False or finally None if the resource couldn't be found. ''' self.requests += 1 if locationName is not None: location = self.locationsByName.get(locationName) else: location = self.locateResource(resType, filename) if location is None: self.log.error('Failed to locate resource %s' % filename) return None # get the full path to query the cache, sticky and preload stores fullPath = location.getResourceFullPath(filename) if fullPath is None: self.log.error('Failed to get full path to resource %s' % filename) return None # resource locations can be sticky if location.sticky: resource = self._getStickyResource(fullPath, resType) if resource is not None: if self.log.isEnabledFor(logging.DEBUG): self.log.debug('Returning sticky resource %s' % fullPath) self.stickyLoads += 1 if not preloading: resource.requested = True return resource.data if not preloading else resource # if the location has a preload flag, then search first in the preload store if location.preload: resource = self._fetchPreloaded(fullPath, location.name) if resource is not None: self.preloadHits += 1 if not preloading: resource.requested = True return resource.data if not preloading else resource else: self.preloadMisses += 1 # then search in our cache # resource = self._cacheLookup(fullPath, location.name) # if resource is not None: # if self.log.isEnabledFor(logging.DEBUG): # self.log.debug('Returning cached instance of resource %s' % fullPath) # if not preloading: # resource.requested = True # return resource.data if not preloading else resource # finally load it from the resource location if ResourcesTypes.isParsedResource(resType): # Convention: construct resource name from the basename of the filename and by dropping the extension. resName = os.path.basename(filename) extIndex = resName.rfind('.') if extIndex >= 0: resName = resName[:extIndex] resData = self._loadParsedResource(resType, resName, fullPath, location) else: if ResourcesTypes.isPandaResource(resType): # for Panda resources we use the BaseLoader resName = filename try: if resType == PanoConstants.RES_TYPE_MODELS: resData = loader.loadModel(fullPath) elif resType == PanoConstants.RES_TYPE_TEXTURES or resType == PanoConstants.RES_TYPE_VIDEOS: resData = loader.loadTexture(fullPath) elif resType == PanoConstants.RES_TYPE_IMAGES: img = PNMImage() img.read(fullPath) resData = img elif resType == PanoConstants.RES_TYPE_MUSIC: resData = loader.loadMusic(fullPath) elif resType == PanoConstants.RES_TYPE_SFX: resData = loader.loadSfx(fullPath) elif resType == PanoConstants.RES_TYPE_SHADERS: resData = Shader.load(fullPath) except Exception: self.log.exception( 'Panda loader failed to load resource %s' % fullPath) return None elif ResourcesTypes.isStreamResource(resType): # we consider character based and binary based streams # by handling stream resources in a special way we can perhaps provide more efficient # handling of streams, i.e. memory mapped files, compressed streams, decryption, etc. resName = filename if resType == PanoConstants.RES_TYPE_SCRIPTS or resType == PanoConstants.RES_TYPE_TEXTS: resData = self._loadCharacterStream(fullPath, location) else: resData = self._loadBinaryStream(fullPath, location) elif ResourcesTypes.isOpaqueResource(resType): # opaque resources perform their own loading, we only load the file's contents without caring # about how it looks and pass it to the read() method. resName = os.path.basename(filename) resData = ResourcesTypes.constructOpaqueResource( resType, resName, filename) opaque = self._loadBinaryStream(fullPath, location) fp = StringIO.StringIO(opaque) resData.read(fp) if resData is None: self.log.error('Failed to load resource %s' % fullPath) return None resource = Resource(resName, resData, resType, fullPath, location.name) resource.sticky = location.sticky resource.preload = location.preload if not preloading: resource.requested = True # consider caching the resource if not resource.sticky and not resource.preload: self._cacheResource(fullPath, resource, location.name) elif resource.sticky: self._addStickyResource(fullPath, resource, location.name) # when we are preloading, return the Resource instance instead return resource.data if not preloading else resource