class ZMap(DirectObject): COLORS = [ VBase3D(0.7, 0.7, 0.9), # AIR VBase3D(0.4, 0.5, 0.1), # DIRT VBase3D(0.6, 0.6, 0.6) # STONE ] def __init__(self, world, app): self.world = world self.image = PNMImage(self.world.width, 256) for z in self.world.zlevels(): for x in range(self.world.height): mix = sum([ ZMap.COLORS[self.world.get_block(x, y, z).substance] for y in range(self.world.height) ], VBase3D(0.0)) color_block = mix / float(self.world.height) #print(color_block) #self.image.setXel(int(x), int(z), mix / float(self.world.height)) self.image.setXel( int(x), int(z), LRGBColorf(color_block[0], color_block[1], color_block[2])) self.texture = Texture() self.texture.load(self.image) self.texture.setMagfilter(Texture.FTNearest) self.texture.setMinfilter(Texture.FTNearest) cm = CardMaker('zmap') cm.setFrame(0.95, 1, -1, 1) cm.setUvRange(Point2(1.0, 1.0), Point2(0.0, 1.0 - self.world.depth / 256.0)) self.zcard = app.render2d.attachNewNode(cm.generate()) self.zcard.setTexture(self.texture) cm = CardMaker('zpointer') cm.setFrame(0, 0.05, 0, 1.0 / self.world.depth) self.zpointer = app.render2d.attachNewNode(cm.generate()) self.zpointer.setColorScale(1.0, 0.0, 0.0, 0.4) self.accept('slice-changed', self.slice_changed) def slice_changed(self, slice, explore): self.zpointer.setPos(0.95, 0.0, slice * 2.0 / self.world.depth - 1.0)
def __init__(self): ShowBase.__init__(self) base.setBackgroundColor(0, 0, 0) # render.setShaderAuto() img = PNMImage("data/textures/vehicle01_body.png") for y in range(img.getReadYSize()): for x in range(img.getReadXSize()): r, g, b = img.getXel(x, y) h, s, v = colorsys.rgb_to_hsv(r, g, b) h += 0.9 # print "#", r,g,b r, g, b = colorsys.hsv_to_rgb(h, s, v) img.setXel(x, y, r, g, b) # print "-", r,g,b img.write("data/textures/vehicle01_body2.png")
class WaterManager(DebugObject): """ Simple wrapper arround WaterDisplacement which combines 3 displacement maps into one, and also generates a normal map """ def __init__(self): DebugObject.__init__(self, "WaterManager") self.options = OceanOptions() self.options.size = 512 self.options.windDir.normalize() self.options.waveAmplitude *= 1e-7 self.displacementTex = Texture("Displacement") self.displacementTex.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.normalTex = Texture("Normal") self.normalTex.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.combineShader = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/Combine.compute") self.ptaTime = PTAFloat.emptyArray(1) # Create a gaussian random texture, as shaders aren't well suited # for that setRandomSeed(523) self.randomStorage = PNMImage(self.options.size, self.options.size, 4) self.randomStorage.setMaxval((2 ** 16) - 1) for x in xrange(self.options.size): for y in xrange(self.options.size): rand1 = self._getGaussianRandom() / 10.0 + 0.5 rand2 = self._getGaussianRandom() / 10.0 + 0.5 self.randomStorage.setXel(x, y, float(rand1), float(rand2), 0) self.randomStorage.setAlpha(x, y, 1.0) self.randomStorageTex = Texture("RandomStorage") self.randomStorageTex.load(self.randomStorage) self.randomStorageTex.setFormat(Texture.FRgba16) self.randomStorageTex.setMinfilter(Texture.FTNearest) self.randomStorageTex.setMagfilter(Texture.FTNearest) # Create the texture wwhere the intial height (H0 + Omega0) is stored. self.texInitialHeight = Texture("InitialHeight") self.texInitialHeight.setup2dTexture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.texInitialHeight.setMinfilter(Texture.FTNearest) self.texInitialHeight.setMagfilter(Texture.FTNearest) # Create the shader which populates the initial height texture self.shaderInitialHeight = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/InitialHeight.compute") self.nodeInitialHeight = NodePath("initialHeight") self.nodeInitialHeight.setShader(self.shaderInitialHeight) self.nodeInitialHeight.setShaderInput("dest", self.texInitialHeight) self.nodeInitialHeight.setShaderInput( "N", LVecBase2i(self.options.size)) self.nodeInitialHeight.setShaderInput( "patchLength", self.options.patchLength) self.nodeInitialHeight.setShaderInput("windDir", self.options.windDir) self.nodeInitialHeight.setShaderInput( "windSpeed", self.options.windSpeed) self.nodeInitialHeight.setShaderInput( "waveAmplitude", self.options.waveAmplitude) self.nodeInitialHeight.setShaderInput( "windDependency", self.options.windDependency) self.nodeInitialHeight.setShaderInput( "randomTex", self.randomStorageTex) self.attrInitialHeight = self.nodeInitialHeight.getAttrib(ShaderAttrib) self.heightTextures = [] for i in xrange(3): tex = Texture("Height") tex.setup2dTexture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) tex.setMinfilter(Texture.FTNearest) tex.setMagfilter(Texture.FTNearest) tex.setWrapU(Texture.WMClamp) tex.setWrapV(Texture.WMClamp) self.heightTextures.append(tex) # Also create the shader which updates the spectrum self.shaderUpdate = Shader.loadCompute(Shader.SLGLSL, "Shader/WaterFFT/Update.compute") self.nodeUpdate = NodePath("update") self.nodeUpdate.setShader(self.shaderUpdate) self.nodeUpdate.setShaderInput("outH0x", self.heightTextures[0]) self.nodeUpdate.setShaderInput("outH0y", self.heightTextures[1]) self.nodeUpdate.setShaderInput("outH0z", self.heightTextures[2]) self.nodeUpdate.setShaderInput("initialHeight", self.texInitialHeight) self.nodeUpdate.setShaderInput("N", LVecBase2i(self.options.size)) self.nodeUpdate.setShaderInput("time", self.ptaTime) self.attrUpdate = self.nodeUpdate.getAttrib(ShaderAttrib) # Create 3 FFTs self.fftX = GPUFFT(self.options.size, self.heightTextures[0], self.options.normalizationFactor) self.fftY = GPUFFT(self.options.size, self.heightTextures[1], self.options.normalizationFactor) self.fftZ = GPUFFT(self.options.size, self.heightTextures[2], self.options.normalizationFactor) self.combineNode = NodePath("Combine") self.combineNode.setShader(self.combineShader) self.combineNode.setShaderInput( "displacementX", self.fftX.getResultTexture()) self.combineNode.setShaderInput( "displacementY", self.fftY.getResultTexture()) self.combineNode.setShaderInput( "displacementZ", self.fftZ.getResultTexture()) self.combineNode.setShaderInput("normalDest", self.normalTex) self.combineNode.setShaderInput( "displacementDest", self.displacementTex) self.combineNode.setShaderInput( "N", LVecBase2i(self.options.size)) self.combineNode.setShaderInput( "choppyScale", self.options.choppyScale) self.combineNode.setShaderInput( "gridLength", self.options.patchLength) # Store only the shader attrib as this is way faster self.attrCombine = self.combineNode.getAttrib(ShaderAttrib) def _getGaussianRandom(self): """ Returns a gaussian random number """ u1 = generateRandom() u2 = generateRandom() if u1 < 1e-6: u1 = 1e-6 return sqrt(-2 * log(u1)) * cos(2 * pi * u2) def setup(self): """ Setups the manager """ Globals.base.graphicsEngine.dispatch_compute( (self.options.size / 16, self.options.size / 16, 1), self.attrInitialHeight, Globals.base.win.get_gsg()) def getDisplacementTexture(self): """ Returns the displacement texture, storing the 3D Displacement in the RGB channels """ return self.displacementTex def getNormalTexture(self): """ Returns the normal texture, storing the normal in world space in the RGB channels """ return self.normalTex def update(self): """ Updates the displacement / normal map """ self.ptaTime[0] = 1 + \ Globals.clock.getFrameTime() * self.options.timeScale Globals.base.graphicsEngine.dispatch_compute( (self.options.size / 16, self.options.size / 16, 1), self.attrUpdate, Globals.base.win.get_gsg()) self.fftX.execute() self.fftY.execute() self.fftZ.execute() # Execute the shader which combines the 3 displacement maps into # 1 displacement texture and 1 normal texture. We could use dFdx in # the fragment shader, however that gives no accurate results as # dFdx returns the same value for a 2x2 pixel block Globals.base.graphicsEngine.dispatch_compute( (self.options.size / 16, self.options.size / 16, 1), self.attrCombine, Globals.base.win.get_gsg())
class WaterManager(DebugObject): """ Simple wrapper arround WaterDisplacement which combines 3 displacement maps into one, and also generates a normal map """ def __init__(self): DebugObject.__init__(self, "WaterManager") self.options = OceanOptions() self.options.size = 512 self.options.windDir.normalize() self.options.waveAmplitude *= 1e-7 self.displacementTex = Texture("Displacement") self.displacementTex.setup2dTexture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.normalTex = Texture("Normal") self.normalTex.setup2dTexture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.combineShader = BetterShader.loadCompute( "Shader/Water/Combine.compute") self.ptaTime = PTAFloat.emptyArray(1) # Create a gaussian random texture, as shaders aren't well suited # for that setRandomSeed(523) self.randomStorage = PNMImage(self.options.size, self.options.size, 4) self.randomStorage.setMaxval((2**16) - 1) for x in xrange(self.options.size): for y in xrange(self.options.size): rand1 = self._getGaussianRandom() / 10.0 + 0.5 rand2 = self._getGaussianRandom() / 10.0 + 0.5 self.randomStorage.setXel(x, y, LVecBase3d(rand1, rand2, 0)) self.randomStorage.setAlpha(x, y, 1.0) self.randomStorageTex = Texture("RandomStorage") self.randomStorageTex.load(self.randomStorage) self.randomStorageTex.setFormat(Texture.FRgba16) self.randomStorageTex.setMinfilter(Texture.FTNearest) self.randomStorageTex.setMagfilter(Texture.FTNearest) # Create the texture wwhere the intial height (H0 + Omega0) is stored. self.texInitialHeight = Texture("InitialHeight") self.texInitialHeight.setup2dTexture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.texInitialHeight.setMinfilter(Texture.FTNearest) self.texInitialHeight.setMagfilter(Texture.FTNearest) # Create the shader which populates the initial height texture self.shaderInitialHeight = BetterShader.loadCompute( "Shader/Water/InitialHeight.compute") self.nodeInitialHeight = NodePath("initialHeight") self.nodeInitialHeight.setShader(self.shaderInitialHeight) self.nodeInitialHeight.setShaderInput("dest", self.texInitialHeight) self.nodeInitialHeight.setShaderInput("N", LVecBase2i(self.options.size)) self.nodeInitialHeight.setShaderInput("patchLength", self.options.patchLength) self.nodeInitialHeight.setShaderInput("windDir", self.options.windDir) self.nodeInitialHeight.setShaderInput("windSpeed", self.options.windSpeed) self.nodeInitialHeight.setShaderInput("waveAmplitude", self.options.waveAmplitude) self.nodeInitialHeight.setShaderInput("windDependency", self.options.windDependency) self.nodeInitialHeight.setShaderInput("randomTex", self.randomStorageTex) self.attrInitialHeight = self.nodeInitialHeight.getAttrib(ShaderAttrib) self.heightTextures = [] for i in xrange(3): tex = Texture("Height") tex.setup2dTexture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) tex.setMinfilter(Texture.FTNearest) tex.setMagfilter(Texture.FTNearest) tex.setWrapU(Texture.WMClamp) tex.setWrapV(Texture.WMClamp) self.heightTextures.append(tex) # Also create the shader which updates the spectrum self.shaderUpdate = BetterShader.loadCompute( "Shader/Water/Update.compute") self.nodeUpdate = NodePath("update") self.nodeUpdate.setShader(self.shaderUpdate) self.nodeUpdate.setShaderInput("outH0x", self.heightTextures[0]) self.nodeUpdate.setShaderInput("outH0y", self.heightTextures[1]) self.nodeUpdate.setShaderInput("outH0z", self.heightTextures[2]) self.nodeUpdate.setShaderInput("initialHeight", self.texInitialHeight) self.nodeUpdate.setShaderInput("N", LVecBase2i(self.options.size)) self.nodeUpdate.setShaderInput("time", self.ptaTime) self.attrUpdate = self.nodeUpdate.getAttrib(ShaderAttrib) # Create 3 FFTs self.fftX = GPUFFT(self.options.size, self.heightTextures[0], self.options.normalizationFactor) self.fftY = GPUFFT(self.options.size, self.heightTextures[1], self.options.normalizationFactor) self.fftZ = GPUFFT(self.options.size, self.heightTextures[2], self.options.normalizationFactor) self.combineNode = NodePath("Combine") self.combineNode.setShader(self.combineShader) self.combineNode.setShaderInput("displacementX", self.fftX.getResultTexture()) self.combineNode.setShaderInput("displacementY", self.fftY.getResultTexture()) self.combineNode.setShaderInput("displacementZ", self.fftZ.getResultTexture()) self.combineNode.setShaderInput("normalDest", self.normalTex) self.combineNode.setShaderInput("displacementDest", self.displacementTex) self.combineNode.setShaderInput("N", LVecBase2i(self.options.size)) self.combineNode.setShaderInput("choppyScale", self.options.choppyScale) self.combineNode.setShaderInput("gridLength", self.options.patchLength) # Store only the shader attrib as this is way faster self.attrCombine = self.combineNode.getAttrib(ShaderAttrib) def _getGaussianRandom(self): """ Returns a gaussian random number """ u1 = generateRandom() u2 = generateRandom() if u1 < 1e-6: u1 = 1e-6 return sqrt(-2 * log(u1)) * cos(2 * pi * u2) def setup(self): """ Setups the manager """ Globals.base.graphicsEngine.dispatch_compute( (self.options.size / 16, self.options.size / 16, 1), self.attrInitialHeight, Globals.base.win.get_gsg()) def getDisplacementTexture(self): """ Returns the displacement texture, storing the 3D Displacement in the RGB channels """ return self.displacementTex def getNormalTexture(self): """ Returns the normal texture, storing the normal in world space in the RGB channels """ return self.normalTex def update(self): """ Updates the displacement / normal map """ self.ptaTime[0] = 1 + \ Globals.clock.getFrameTime() * self.options.timeScale Globals.base.graphicsEngine.dispatch_compute( (self.options.size / 16, self.options.size / 16, 1), self.attrUpdate, Globals.base.win.get_gsg()) self.fftX.execute() self.fftY.execute() self.fftZ.execute() # Execute the shader which combines the 3 displacement maps into # 1 displacement texture and 1 normal texture. We could use dFdx in # the fragment shader, however that gives no accurate results as # dFdx returns the same value for a 2x2 pixel block Globals.base.graphicsEngine.dispatch_compute( (self.options.size / 16, self.options.size / 16, 1), self.attrCombine, Globals.base.win.get_gsg())
class TerrainManager(DirectObject.DirectObject): def __init__(self): self.accept("mouse1", self.lclick) self.waterType = 2 self.water = None self.citycolors = {0: VBase3D(1, 1, 1)} self.accept('generateRegion', self.generateWorld) self.accept('regenerateRegion', self.regenerateWorld) self.accept("regionView_normal", self.setSurfaceTextures) self.accept("regionView_owners", self.setOwnerTextures) self.accept("regionView_foundNew", self.regionViewFound) self.accept("updateRegion", self.updateRegion) self.accept("enterCityView", self.enterCity) # View: 0, region, # cityid self.view = 0 self.ownerview = False def lclick(self): cell = picker.getMouseCell() print "Cell:", cell blockCoords = self.terrain.getBlockFromPos(cell[0], cell[1]) block = self.terrain.getBlockNodePath(blockCoords[0], blockCoords[1]) print "Block coords:", blockCoords print "NodePath:", block print "Elevation:", self.terrain.getElevation(cell[0], cell[1]) if not self.view: messenger.send("clickForCity", [cell]) def switchWater(self): print "Switch Water" self.waterType += 1 if self.waterType > 2: self.waterType = 0 self.generateWater(self.waterType) def generateWorld(self, heightmap, tiles, cities, container): self.heightmap = heightmap self.terrain = PagedGeoMipTerrain("surface") #self.terrain = GeoMipTerrain("surface") self.terrain.setHeightfield(self.heightmap) #self.terrain.setFocalPoint(base.camera) self.terrain.setBruteforce(True) self.terrain.setBlockSize(64) self.terrain.generate() root = self.terrain.getRoot() root.reparentTo(render) #root.setSz(100) self.terrain.setSz(100) messenger.send('makePickable', [root]) if self.heightmap.getXSize() > self.heightmap.getYSize(): self.size = self.heightmap.getXSize() - 1 else: self.size = self.heightmap.getYSize() - 1 self.xsize = self.heightmap.getXSize() - 1 self.ysize = self.heightmap.getYSize() - 1 # Set multi texture # Source http://www.panda3d.org/phpbb2/viewtopic.php?t=4536 self.generateSurfaceTextures() self.generateWaterMap() self.generateOwnerTexture(tiles, cities) #self.terrain.makeTextureMap() colormap = PNMImage(heightmap.getXSize() - 1, heightmap.getYSize() - 1) colormap.addAlpha() slopemap = self.terrain.makeSlopeImage() for x in range(0, colormap.getXSize()): for y in range(0, colormap.getYSize()): # Else if statements used to make sure one channel is used per pixel # Also for some optimization # Snow. We do things funky here as alpha will be 1 already. if heightmap.getGrayVal(x, y) < 200: colormap.setAlpha(x, y, 0) else: colormap.setAlpha(x, y, 1) # Beach. Estimations from http://www.simtropolis.com/omnibus/index.cfm/Main.SimCity_4.Custom_Content.Custom_Terrains_and_Using_USGS_Data if heightmap.getGrayVal(x, y) < 62: colormap.setBlue(x, y, 1) # Rock elif slopemap.getGrayVal(x, y) > 170: colormap.setRed(x, y, 1) else: colormap.setGreen(x, y, 1) self.colorTexture = Texture() self.colorTexture.load(colormap) self.colorTS = TextureStage('color') self.colorTS.setSort(0) self.colorTS.setPriority(1) self.setSurfaceTextures() self.generateWater(2) taskMgr.add(self.updateTerrain, "updateTerrain") print "Done with terrain generation" messenger.send("finishedTerrainGen", [[self.xsize, self.ysize]]) self.terrain.getRoot().analyze() self.accept("h", self.switchWater) def regenerateWorld(self): '''Regenerates world, often upon city exit.''' self.terrain.generate() root = self.terrain.getRoot() root.reparentTo(render) self.terrain.setSz(100) messenger.send('makePickable', [root]) # Set multi texture # Source http://www.panda3d.org/phpbb2/viewtopic.php?t=4536 #self.generateSurfaceTextures() self.generateWaterMap() self.setSurfaceTextures() self.generateWater(2) print "Done with terrain regeneration" messenger.send("finishedTerrainGen", [[self.xsize, self.ysize]]) def generateWaterMap(self): ''' Iterate through every pix of color map. This will be very slow so until faster method is developed, use sparingly getXSize returns pixels length starting with 1, subtract 1 for obvious reasons We also slip in checking for the water card size, which should only change when the color map does ''' print "GenerateWaterMap" self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax = -1, 0, -1, 0 for x in range(0, self.heightmap.getXSize() - 1): for y in range(0, self.heightmap.getYSize() - 1): # Else if statements used to make sure one channel is used per pixel # Also for some optimization # Snow. We do things funky here as alpha will be 1 already. if self.heightmap.getGrayVal(x, y) < 62: # Water card dimensions here # Y axis flipped from texture space to world space if self.waterXMin == -1 or x < self.waterXMin: self.waterXMin = x if not self.waterYMax: self.waterYMax = y if y < self.waterYMax: self.waterYMax = y if x > self.waterXMax: self.waterXMax = x if y > self.waterYMin: self.waterYMin = y # Transform y coords self.waterYMin = self.size - 64 - self.waterYMin self.waterYMax = self.size - 64 - self.waterYMax def generateOwnerTexture(self, tiles, cities): '''Generates a simple colored texture to be applied to the city info region overlay. Due to different coordinate systems (terrain org bottom left, texture top left) some conversions are needed, Also creates and sends a citylabels dict for the region view ''' self.citymap = PNMImage(self.xsize, self.ysize) citylabels = cities scratch = {} # Setup for city labels for ident in cities: scratch[ident] = [] # conversion for y axis ycon = [] s = self.ysize - 1 for y in range(self.ysize): ycon.append(s) s -= 1 for ident, city in cities.items(): if ident not in self.citycolors: self.citycolors[ident] = VBase3D(random.random(), random.random(), random.random()) for tile in tiles: self.citymap.setXel(tile.coords[0], ycon[tile.coords[1]], self.citycolors[tile.cityid]) # Scratch for labeling if tile.cityid: scratch[tile.cityid].append((tile.coords[0], tile.coords[1])) for ident, values in scratch.items(): xsum = 0 ysum = 0 n = 0 for coords in values: xsum += coords[0] ysum += coords[1] n += 1 xavg = xsum / n yavg = ysum / n print "Elevation:", self.terrain.getElevation(xavg, yavg) z = self.terrain.getElevation(xavg, yavg) * 100 citylabels[ident]["position"] = (xavg, yavg, z + 15) print "Citylabels:", citylabels messenger.send("updateCityLabels", [citylabels, self.terrain]) def generateSurfaceTextures(self): # Textureize self.grassTexture = loader.loadTexture("Textures/grass.png") self.grassTS = TextureStage('grass') self.grassTS.setSort(1) self.rockTexture = loader.loadTexture("Textures/rock.jpg") self.rockTS = TextureStage('rock') self.rockTS.setSort(2) self.rockTS.setCombineRgb(TextureStage.CMAdd, TextureStage.CSLastSavedResult, TextureStage.COSrcColor, TextureStage.CSTexture, TextureStage.COSrcColor) self.sandTexture = loader.loadTexture("Textures/sand.jpg") self.sandTS = TextureStage('sand') self.sandTS.setSort(3) self.sandTS.setPriority(5) self.snowTexture = loader.loadTexture("Textures/ice.png") self.snowTS = TextureStage('snow') self.snowTS.setSort(4) self.snowTS.setPriority(0) # Grid for city placement and guide and stuff self.gridTexture = loader.loadTexture("Textures/grid.png") self.gridTexture.setWrapU(Texture.WMRepeat) self.gridTexture.setWrapV(Texture.WMRepeat) self.gridTS = TextureStage('grid') self.gridTS.setSort(5) self.gridTS.setPriority(10) def enterCity(self, ident, city, position, tiles): '''Identifies which terrain blocks city belogs to and disables those that are not A lot of uneeded for loops in here. Will need to find a better way later.''' #root = self.terrain.getRoot() children = [] for terrain in self.terrain.terrains: root = terrain.getRoot() children += root.getChildren() keepBlocks = [] # Reset water dimentions self.waterXMin = 0 self.waterXMax = 0 self.waterYMin = 0 self.waterYMax = 0 for tile in tiles: blockCoords = self.terrain.getBlockFromPos(tile.coords[0], tile.coords[1]) block = self.terrain.getBlockNodePath(blockCoords[0], blockCoords[1]) if block not in keepBlocks: keepBlocks.append(block) if self.heightmap.getGrayVal(tile.coords[0], self.size - tile.coords[1]) < 62: # Water card dimensions here # Y axis flipped from texture space to world space if not self.waterXMin: self.waterXMin = tile.coords[0] if not self.waterYMin: self.waterYMin = tile.coords[1] if tile.coords[1] > self.waterYMax: self.waterYMax = tile.coords[1] if tile.coords[0] > self.waterXMax: self.waterXMax = tile.coords[0] if tile.coords[0] < self.waterXMin: self.waterXMin = tile.coords[0] if tile.coords[1] < self.waterYMin: self.waterYMin = tile.coords[1] for child in children: if child not in keepBlocks: child.detachNode() self.view = ident self.generateWater(2) def newTerrainOverlay(self, task): root = self.terrain.getRoot() position = picker.getMouseCell() if position: # Check to make sure we do not go out of bounds if position[0] < 32: position = (32, position[1]) elif position[0] > self.xsize - 32: position = (self.xsize - 32, position[1]) if position[1] < 32: position = (position[0], 32) elif position[1] > self.ysize - 32: position = (position[0], self.size - 32) root.setTexOffset(self.tileTS, -(position[0] - 32) / 64, -(position[1] - 32) / 64) return task.cont def regionViewFound(self): '''Gui for founding a new city!''' self.setOwnerTextures() root = self.terrain.getRoot() task = taskMgr.add(self.newTerrainOverlay, "newTerrainOverlay") tileTexture = loader.loadTexture("Textures/tile.png") tileTexture.setWrapU(Texture.WMClamp) tileTexture.setWrapV(Texture.WMClamp) self.tileTS = TextureStage('tile') self.tileTS.setSort(6) self.tileTS.setMode(TextureStage.MDecal) #self.tileTS.setColor(Vec4(1,0,1,1)) root.setTexture(self.tileTS, tileTexture) root.setTexScale(self.tileTS, self.terrain.xchunks, self.terrain.ychunks) self.acceptOnce("mouse1", self.regionViewFound2) self.acceptOnce("escape", self.cancelRegionViewFound) def regionViewFound2(self): '''Grabs cell location for founding. The texture coordinate is used as the mouse may enter an out of bounds area. ''' root = self.terrain.getRoot() root_position = root.getTexOffset(self.tileTS) # We offset the position of the texture, so we will now put the origin of the new city not on mouse cursor but the "bottom left" of it. Just need to add 32 to get other edge position = [ int(abs(root_position[0] * 64)), int(abs(root_position[1] * 64)) ] self.cancelRegionViewFound() messenger.send("found_city_name", [position]) def cancelRegionViewFound(self): taskMgr.remove("newTerrainOverlay") root = self.terrain.getRoot() root.clearTexture(self.tileTS) # Restore original mouse function self.accept("mouse1", self.lclick) messenger.send("showRegionGUI") def setSurfaceTextures(self): self.ownerview = False root = self.terrain.getRoot() root.clearTexture() #self.terrain.setTextureMap() root.setTexture(self.colorTS, self.colorTexture) root.setTexture(self.grassTS, self.grassTexture) root.setTexScale(self.grassTS, self.size / 8, self.size / 8) root.setTexture(self.rockTS, self.rockTexture) root.setTexScale(self.rockTS, self.size / 8, self.size / 8) root.setTexture(self.sandTS, self.sandTexture) root.setTexScale(self.sandTS, self.size / 8, self.size / 8) root.setTexture(self.snowTS, self.snowTexture) root.setTexScale(self.snowTS, self.size / 8, self.size / 8) root.setTexture(self.gridTS, self.gridTexture) root.setTexScale(self.gridTS, self.xsize, self.ysize) root.setShaderInput('size', self.xsize, self.ysize, self.size, self.size) root.setShader(loader.loadShader('Shaders/terraintexture.sha')) def setOwnerTextures(self): self.ownerview = True root = self.terrain.getRoot() root.clearShader() root.clearTexture() cityTexture = Texture() cityTexture.load(self.citymap) cityTS = TextureStage('citymap') cityTS.setSort(0) root.setTexture(self.gridTS, self.gridTexture) root.setTexScale(self.gridTS, self.terrain.xchunks, self.terrain.ychunks) root.setTexture(cityTS, cityTexture, 1) def updateRegion(self, heightmap, tiles, cities): self.generateOwnerTexture(tiles, cities) if self.ownerview: self.setOwnerTextures() def updateTerrain(self, task): '''Updates terrain and water''' self.terrain.update() # Water if self.waterType is 2: pos = base.camera.getPos() render.setShaderInput('time', task.time) mc = base.camera.getMat() self.water.changeCameraPos(pos, mc) self.water.changeCameraPos(pos, mc) #print "Render diagnostics" #render.analyze() #base.cTrav.showCollisions(render) return task.cont def generateWater(self, style): print "Generate Water:", self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax '''Generates water style 0: blue card style 1: reflective card style 2: reflective card with shaders ''' self.waterHeight = 22.0 if self.water: self.water.removeNode() if style is 0: cm = CardMaker("water") #cm.setFrame(-1, 1, -1, 1) cm.setFrame(self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax) cm.setColor(0, 0, 1, 0.9) self.water = render.attachNewNode(cm.generate()) if self.waterYMax > self.waterXMax: size = self.waterYMax else: size = self.waterXMax self.water.lookAt(0, 0, -1) self.water.setZ(self.waterHeight) messenger.send('makePickable', [self.water]) elif style is 1: # From Prosoft's super awesome terrain demo cm = CardMaker("water") #cm.setFrame(-1, 1, -1, 1) cm.setFrame(self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax) self.water = render.attachNewNode(cm.generate()) if self.waterYMax > self.waterXMax: size = self.waterYMax else: size = self.waterXMax #self.water.setScale(size) self.water.lookAt(0, 0, -1) self.water.setZ(self.waterHeight) self.water.setShaderOff(1) self.water.setLightOff(1) self.water.setAlphaScale(0.5) self.water.setTransparency(TransparencyAttrib.MAlpha) wbuffer = base.win.makeTextureBuffer("water", 512, 512) wbuffer.setClearColorActive(True) wbuffer.setClearColor(base.win.getClearColor()) self.wcamera = base.makeCamera(wbuffer) self.wcamera.reparentTo(render) self.wcamera.node().setLens(base.camLens) self.wcamera.node().setCameraMask(BitMask32.bit(1)) self.water.hide(BitMask32.bit(1)) wtexture = wbuffer.getTexture() wtexture.setWrapU(Texture.WMClamp) wtexture.setWrapV(Texture.WMClamp) wtexture.setMinfilter(Texture.FTLinearMipmapLinear) self.wplane = Plane(Vec3(0, 0, 1), Point3(0, 0, self.water.getZ())) wplanenp = render.attachNewNode(PlaneNode("water", self.wplane)) tmpnp = NodePath("StateInitializer") tmpnp.setClipPlane(wplanenp) tmpnp.setAttrib(CullFaceAttrib.makeReverse()) self.wcamera.node().setInitialState(tmpnp.getState()) self.water.projectTexture(TextureStage("reflection"), wtexture, self.wcamera) messenger.send('makePickable', [self.water]) elif style is 2: # From Clcheung just as super awesome demomaster self.water_level = Vec4(0.0, 0.0, self.waterHeight, 1.0) self.water = water.WaterNode(self.waterXMin, self.waterYMin, self.waterXMax, self.waterYMax, self.water_level.getZ()) self.water.setStandardControl() self.water.changeParams(None) wl = self.water_level wl.setZ(wl.getZ() - 0.05) #root.setShaderInput('waterlevel', self.water_level) render.setShaderInput('time', 0) messenger.send('makePickable', [self.water.waterNP])
def generateNode(self, name, DB, parentNode): log.debug("Setting up " + name) bodyEntity = sandbox.createEntity() component = CelestialComponent() if DB["type"] == "solid": body = Body(name) elif DB["type"] == "moon": body = Body(name) body.kind = "moon" elif DB["type"] == "star": body = Star(name) body.absoluteM = DB["absolute magnitude"] body.spectral = DB["spectral"] elif DB["type"] == "barycenter": body = BaryCenter(name) component.kind = TYPES[DB["type"]] if DB["type"] != "barycenter": component.mass = DB["mass"] body.radius = DB["radius"] body.rotation = DB["rotation"] if "orbit" in DB: component.orbit = DB["orbit"] body.period = DB["period"] # body.setPos(self.get2DBodyPosition(component, universals.day)) component.truePos = self.get2DBodyPosition(component, universals.day) if name == "Earth": # universals.spawn = component.truePos + LPoint3d(0, 6671, 0) universals.spawn = component.truePos + LPoint3d(6671, 0, 0) if parentNode == universals.solarSystemRoot: universals.defaultSOIid = bodyEntity.id component.soi = 0 elif DB["type"] != "star" or DB["type"] != "barycenter": component.soi = self.getSOI(component.mass, self.bodies[0].mass, component.orbit["a"]) body.type = DB["type"] body.reparentTo(parentNode) component.nodePath = body self.bodies.append(component) bodyEntity.addComponent(component) if universals.runClient and DB["type"] == "star": component = graphicsComponents.RenderComponent() # component.mesh = NodePath(name) # self.sphere.copyTo(component.mesh) # component.mesh = shapeGenerator.Sphere(body.radius, 128, name) component.mesh = shapeGenerator.Sphere(body.radius, 64, name) # component.mesh.setScale(body.radius) component.mesh.reparentTo(sandbox.base.render) sandbox.send("makePickable", [component.mesh]) # texture = sandbox.base.loader.loadTexture('planets/' + DB['texture']) # texture.setMinfilter(Texture.FTLinearMipmapLinear) # ts1 = TextureStage('textures1') # ts1.setMode(TextureStage.MGlow) # component.mesh.setTexture(ts1, texture) # component.mesh.setTexture(texture, 1) component.light = component.mesh.attachNewNode(PointLight("sunPointLight")) component.light.node().setColor(Vec4(1, 1, 1, 1)) sandbox.base.render.setLight(component.light) bodyEntity.addComponent(component) # Shader test componentStar = graphicsComponents.StarRender() componentStar.noise_texture = Texture("noise") componentStar.noise_texture.setup2dTexture() img = PNMImage(1024, 1024) for y in range(1024): for x in range(1024): img.setXel(x, y, componentStar.noise.noise(x, y)) # print componentStar.noise.noise(x, y) componentStar.noise_texture.load(img) # componentStar.noise_texture.write('test.png') # component.mesh.setTexture(componentStar.noise_texture, 1) texture = sandbox.base.loader.loadTexture("planets/" + DB["texture"]) ts1 = TextureStage("textures1") ts1.setMode(TextureStage.MGlow) component.mesh.setTexture(ts1, componentStar.noise_texture) # component.mesh.setTexture(ts1, texture) component.mesh.setShaderInput("time", universals.get_day_in_seconds()) shaders = Shader.load(Shader.SLGLSL, "vortexVertex.glsl", "starFrag.glsl") component.mesh.setShader(shaders) sandbox.send("makePickable", [component.mesh]) if universals.runClient and (DB["type"] == "solid" or DB["type"] == "moon"): component = graphicsComponents.RenderComponent() # component.mesh = shapeGenerator.Sphere(body.radius, 128, name) # component.mesh = shapeGenerator.Sphere(body.radius, 64, name) component.mesh = surface_mesh.make_planet(name=name, scale=body.radius) # sandbox.send('makePickable', [component.mesh]) sandbox.send("makePickable", [component.mesh.node_path]) # component.mesh.setScale(body.radius) component.mesh.reparent_to(sandbox.base.render) # Doing world text text = TextNode("node name") text.setText(name) # textNodePath = component.mesh.attachNewNode(text) textNodePath = component.mesh.node_path.attachNewNode(text) textNodePath.setScale(0.07) component.mesh.set_textures(DB["texture"], night_path=DB["night"], gloss_path=DB["spec"]) component.mesh.set_ambient(1, 1, 1, 1) component.mesh.set_diffuse(1, 1, 1, 1) component.mesh.set_specular(1, 1, 1, 1) component.mesh.set_shininess(100) """if '#' in DB['texture']: component.mesh.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition) component.mesh.setTexProjector(TextureStage.getDefault(), sandbox.base.render, component.mesh) component.mesh.setTexScale(TextureStage.getDefault(), 1, 1, -1) component.mesh.setTexHpr(TextureStage.getDefault(), 90, -18, 90) #self.mesh.setHpr(0, 90, 0) texture = loader.loadCubeMap('planets/' + DB['texture']) else: texture = sandbox.base.loader.loadTexture('planets/' + DB['texture']) #texture.setMinfilter(Texture.FTLinearMipmapLinear) component.mesh.setTexture(texture, 1)""" """if "atmosphere" in DB: component.atmosphere = shapeGenerator.Sphere(-1, 128) component.atmosphere.reparentTo(render) component.atmosphere.setScale(body.radius * 1.025) outerRadius = component.atmosphere.getScale().getX() scale = 1 / (outerRadius - component.body.getScale().getX()) component.atmosphere.setShaderInput("fOuterRadius", outerRadius) component.atmosphere.setShaderInput("fInnerRadius", component.mesh.getScale().getX()) component.atmosphere.setShaderInput("fOuterRadius2", outerRadius * outerRadius) component.atmosphere.setShaderInput("fInnerRadius2", component.mesh.getScale().getX() * component.mesh.getScale().getX()) component.atmosphere.setShaderInput("fKr4PI", 0.000055 * 4 * 3.14159) component.atmosphere.setShaderInput("fKm4PI", 0.000015 * 4 * 3.14159) component.atmosphere.setShaderInput("fScale", scale) component.atmosphere.setShaderInput("fScaleDepth", 0.25) component.atmosphere.setShaderInput("fScaleOverScaleDepth", scale / 0.25) # Currently hard coded in shader component.atmosphere.setShaderInput("fSamples", 10.0) component.atmosphere.setShaderInput("nSamples", 10) # These do sunsets and sky colors # Brightness of sun ESun = 15 # Reyleight Scattering (Main sky colors) component.atmosphere.setShaderInput("fKrESun", 0.000055 * ESun) # Mie Scattering -- Haze and sun halos component.atmosphere.setShaderInput("fKmESun", 0.000015 * ESun) # Color of sun component.atmosphere.setShaderInput("v3InvWavelength", 1.0 / math.pow(0.650, 4), 1.0 / math.pow(0.570, 4), 1.0 / math.pow(0.465, 4)) #component.atmosphere.setShader(Shader.load("atmo.cg"))""" bodyEntity.addComponent(component) log.info(name + " set Up") if "bodies" in DB: for bodyName, bodyDB in DB["bodies"].items(): self.generateNode(bodyName, bodyDB, body)
planet.reparentTo(sandbox.base.render) planet.setPos(5, 20, 0) mesh = shapeGenerator.Sphere(1, 64, 'star') mesh.reparentTo(sandbox.base.render) mesh.setPos(-5, 20, 0) noise = PerlinNoise2(64, 64) texture = Texture('noise') texture.setup2dTexture() img = PNMImage(1024, 1024) for y in range(1024): for x in range(1024): img.setXel(x, y, noise.noise(x, y)) texture.load(img) mesh.setTexture(texture, 1) mesh.setShaderInput('time', 0) #shaders = Shader.load(Shader.SLGLSL, 'vortexVertex.glsl', 'starFrag2.glsl') shaders = Shader.load(Shader.SLGLSL, 'sphereVertex.glsl', 'starFrag2.glsl') mesh.setShader(shaders) light = mesh.attachNewNode(PointLight("sun")) light.node().setColor(Vec4(1, 1, 1, 1)) sandbox.base.render.setLight(light) def time_update(task): mesh.setShaderInput('time', task.time)
if channel == CHANNEL_AO: return img.getRed(x, y) elif channel == CHANNEL_ROUGHNESS: return img.getGreen(x, y) elif channel == CHANNEL_METALLIC: return img.getBlue(x, y) elif channel == CHANNEL_EMISSIVE: return img.getAlpha(x, y) armeFile = raw_input("ARME file: ") armeFilename = Filename.fromOsSpecific(armeFile) img = PNMImage() img.read(armeFilename) for i in range(4): name = getChannelName(i).lower() print("Writing", name) chImg = PNMImage(img.getReadXSize(), img.getReadYSize()) chImg.setNumChannels(1) chImg.setColorType(PNMImageHeader.CTGrayscale) for x in range(img.getReadXSize()): for y in range(img.getReadYSize()): val = getChannel(img, x, y, i) chImg.setXel(x, y, val) chImg.write( Filename(armeFilename.getFullpathWoExtension() + "_" + name + ".png")) print("Done")
def fBm(x, y, per, octs): val = 0 for o in range(octs): val += 0.5**o * noise(x*2**o, y*2**o, per*2**o) return val size, freq, octs, data = 128, 1/32.0, 5, [] p = PNMImage(128, 128) for y in range(size): for x in range(size): #data.append(fBm(x*freq, y*freq, int(size*freq), octs)) p.setXel(x, y, fBm(x*freq, y*freq, int(size*freq), octs)) #skysphere.setTexGen(TextureStage.getDefault(), TexGenAttrib.MEyeSphereMap) tex.load(p) skysphere.setTexture(tex) '''skysphere.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition) skysphere.setTexProjector(TextureStage.getDefault(), sandbox.base.render, skysphere) skysphere.setTexPos(TextureStage.getDefault(), 0.5, 0.5, 0.5) skysphere.setTexScale(TextureStage.getDefault(), 0.2) ''''''mesh = sandbox.base.loader.loadModel('ships/hyperion/hyperion') mesh.setScale(0.001) mesh.reparentTo(sandbox.base.render)'''''' #noise = PerlinNoise3(32, 32, 32, 256, 1) noise = StackedPerlinNoise3(16, 16, 16, 3, 1, 0.5, 256, 1)
def fBm(x, y, per, octs): val = 0 for o in range(octs): val += 0.5**o * noise(x * 2**o, y * 2**o, per * 2**o) return val size, freq, octs, data = 128, 1 / 32.0, 5, [] p = PNMImage(128, 128) for y in range(size): for x in range(size): #data.append(fBm(x*freq, y*freq, int(size*freq), octs)) p.setXel(x, y, fBm(x * freq, y * freq, int(size * freq), octs)) #skysphere.setTexGen(TextureStage.getDefault(), TexGenAttrib.MEyeSphereMap) tex.load(p) skysphere.setTexture(tex) '''skysphere.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition) skysphere.setTexProjector(TextureStage.getDefault(), sandbox.base.render, skysphere) skysphere.setTexPos(TextureStage.getDefault(), 0.5, 0.5, 0.5) skysphere.setTexScale(TextureStage.getDefault(), 0.2) ''' '''mesh = sandbox.base.loader.loadModel('ships/hyperion/hyperion') mesh.setScale(0.001) mesh.reparentTo(sandbox.base.render)''' ''' #noise = PerlinNoise3(32, 32, 32, 256, 1) noise = StackedPerlinNoise3(16, 16, 16, 3, 1, 0.5, 256, 1)
class WaterManager: """ Simple wrapper around WaterDisplacement which combines 3 displacement maps into one, and also generates a normal map """ def __init__(self, water_options): self.options = water_options self.options.size = 512 self.options.wind_dir.normalize() self.options.wave_amplitude *= 1e-7 self.displacement_tex = Texture("Displacement") self.displacement_tex.setup_2d_texture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.normal_tex = Texture("Normal") self.normal_tex.setup_2d_texture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.combine_shader = Shader.load_compute(Shader.SLGLSL, "/$$rp/rpcore/water/shader/combine.compute") self.pta_time = PTAFloat.emptyArray(1) # Create a gaussian random texture, as shaders aren't well suited # for that setRandomSeed(523) self.random_storage = PNMImage(self.options.size, self.options.size, 4) self.random_storage.setMaxval((2 ** 16) - 1) for x in range(self.options.size): for y in range(self.options.size): rand1 = self._get_gaussian_random() / 10.0 + 0.5 rand2 = self._get_gaussian_random() / 10.0 + 0.5 self.random_storage.setXel(x, y, float(rand1), float(rand2), 0) self.random_storage.setAlpha(x, y, 1.0) self.random_storage_tex = Texture("RandomStorage") self.random_storage_tex.load(self.random_storage) self.random_storage_tex.set_format(Texture.FRgba16) self.random_storage_tex.set_minfilter(Texture.FTNearest) self.random_storage_tex.set_magfilter(Texture.FTNearest) # Create the texture wwhere the intial height (H0 + Omega0) is stored. self.tex_initial_height = Texture("InitialHeight") self.tex_initial_height.setup_2d_texture( self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) self.tex_initial_height.set_minfilter(Texture.FTNearest) self.tex_initial_height.set_magfilter(Texture.FTNearest) # Create the shader which populates the initial height texture self.shader_initial_height = Shader.load_compute(Shader.SLGLSL, "/$$rp/rpcore/water/shader/initial_height.compute") self.node_initial_height = NodePath("initialHeight") self.node_initial_height.set_shader(self.shader_initial_height) self.node_initial_height.set_shader_input("dest", self.tex_initial_height) self.node_initial_height.set_shader_input( "N", LVecBase2i(self.options.size)) self.node_initial_height.set_shader_input( "patchLength", self.options.patch_length) self.node_initial_height.set_shader_input("windDir", self.options.wind_dir) self.node_initial_height.set_shader_input( "windSpeed", self.options.wind_speed) self.node_initial_height.set_shader_input( "waveAmplitude", self.options.wave_amplitude) self.node_initial_height.set_shader_input( "windDependency", self.options.wind_dependency) self.node_initial_height.set_shader_input( "randomTex", self.random_storage_tex) self.attr_initial_height = self.node_initial_height.get_attrib(ShaderAttrib) self.height_textures = [] for i in range(3): tex = Texture("Height") tex.setup_2d_texture(self.options.size, self.options.size, Texture.TFloat, Texture.FRgba16) tex.set_minfilter(Texture.FTNearest) tex.set_magfilter(Texture.FTNearest) tex.set_wrap_u(Texture.WMClamp) tex.set_wrap_v(Texture.WMClamp) self.height_textures.append(tex) # Also create the shader which updates the spectrum self.shader_update = Shader.load_compute(Shader.SLGLSL, "/$$rp/rpcore/water/shader/update.compute") self.node_update = NodePath("update") self.node_update.set_shader(self.shader_update) self.node_update.set_shader_input("outH0x", self.height_textures[0]) self.node_update.set_shader_input("outH0y", self.height_textures[1]) self.node_update.set_shader_input("outH0z", self.height_textures[2]) self.node_update.set_shader_input("initialHeight", self.tex_initial_height) self.node_update.set_shader_input("N", LVecBase2i(self.options.size)) self.node_update.set_shader_input("time", self.pta_time) self.attr_update = self.node_update.get_attrib(ShaderAttrib) # Create 3 FFTs self.fftX = GPUFFT(self.options.size, self.height_textures[0], self.options.normalization_factor) self.fftY = GPUFFT(self.options.size, self.height_textures[1], self.options.normalization_factor) self.fftZ = GPUFFT(self.options.size, self.height_textures[2], self.options.normalization_factor) self.combine_node = NodePath("Combine") self.combine_node.set_shader(self.combine_shader) self.combine_node.set_shader_input( "displacementX", self.fftX.get_result_texture()) self.combine_node.set_shader_input( "displacementY", self.fftY.get_result_texture()) self.combine_node.set_shader_input( "displacementZ", self.fftZ.get_result_texture()) self.combine_node.set_shader_input("normalDest", self.normal_tex) self.combine_node.set_shader_input( "displacementDest", self.displacement_tex) self.combine_node.set_shader_input( "N", LVecBase2i(self.options.size)) self.combine_node.set_shader_input( "choppyScale", self.options.choppy_scale) self.combine_node.set_shader_input( "gridLength", self.options.patch_length) # Store only the shader attrib as this is way faster self.attr_combine = self.combine_node.get_attrib(ShaderAttrib) def _get_gaussian_random(self): """ Returns a gaussian random number """ u1 = generateRandom() u2 = generateRandom() if u1 < 1e-6: u1 = 1e-6 return sqrt(-2 * log(u1)) * cos(2 * pi * u2) def setup(self): """ Setups the manager """ Globals.base.graphicsEngine.dispatch_compute( (self.options.size // 16, self.options.size // 16, 1), self.attr_initial_height, Globals.base.win.get_gsg()) def get_displacement_texture(self): """ Returns the displacement texture, storing the 3D Displacement in the RGB channels """ return self.displacement_tex def get_normal_texture(self): """ Returns the normal texture, storing the normal in world space in the RGB channels """ return self.normal_tex def update(self): """ Updates the displacement / normal map """ self.pta_time[0] = 1 + Globals.clock.get_frame_time() * self.options.time_scale Globals.base.graphicsEngine.dispatch_compute( (self.options.size // 16, self.options.size // 16, 1), self.attr_update, Globals.base.win.get_gsg()) self.fftX.execute() self.fftY.execute() self.fftZ.execute() # Execute the shader which combines the 3 displacement maps into # 1 displacement texture and 1 normal texture. We could use dFdx in # the fragment shader, however that gives no accurate results as # dFdx returns the same value for a 2x2 pixel block Globals.base.graphicsEngine.dispatch_compute( (self.options.size // 16, self.options.size // 16, 1), self.attr_combine, Globals.base.win.get_gsg())
class TerrainManager(DirectObject.DirectObject): def __init__(self): self.accept("mouse1", self.lclick) self.waterType = 2 self.water = None self.citycolors = {0: VBase3D(1, 1, 1)} self.accept('generateRegion', self.generateWorld) self.accept('regenerateRegion', self.regenerateWorld) self.accept("regionView_normal", self.setSurfaceTextures) self.accept("regionView_owners", self.setOwnerTextures) self.accept("regionView_foundNew", self.regionViewFound) self.accept("updateRegion", self.updateRegion) self.accept("enterCityView", self.enterCity) # View: 0, region, # cityid self.view = 0 self.ownerview = False def lclick(self): cell = picker.getMouseCell() print "Cell:", cell blockCoords = self.terrain.getBlockFromPos(cell[0], cell[1]) block = self.terrain.getBlockNodePath(blockCoords[0], blockCoords[1]) print "Block coords:", blockCoords print "NodePath:", block print "Elevation:", self.terrain.getElevation(cell[0], cell[1]) if not self.view: messenger.send("clickForCity", [cell]) def switchWater(self): print "Switch Water" self.waterType += 1 if self.waterType > 2: self.waterType = 0 self.generateWater(self.waterType) def generateWorld(self, heightmap, tiles, cities, container): self.heightmap = heightmap self.terrain = PagedGeoMipTerrain("surface") #self.terrain = GeoMipTerrain("surface") self.terrain.setHeightfield(self.heightmap) #self.terrain.setFocalPoint(base.camera) self.terrain.setBruteforce(True) self.terrain.setBlockSize(64) self.terrain.generate() root = self.terrain.getRoot() root.reparentTo(render) #root.setSz(100) self.terrain.setSz(100) messenger.send('makePickable', [root]) if self.heightmap.getXSize() > self.heightmap.getYSize(): self.size = self.heightmap.getXSize()-1 else: self.size = self.heightmap.getYSize()-1 self.xsize = self.heightmap.getXSize()-1 self.ysize = self.heightmap.getYSize()-1 # Set multi texture # Source http://www.panda3d.org/phpbb2/viewtopic.php?t=4536 self.generateSurfaceTextures() self.generateWaterMap() self.generateOwnerTexture(tiles, cities) #self.terrain.makeTextureMap() colormap = PNMImage(heightmap.getXSize()-1, heightmap.getYSize()-1) colormap.addAlpha() slopemap = self.terrain.makeSlopeImage() for x in range(0, colormap.getXSize()): for y in range(0, colormap.getYSize()): # Else if statements used to make sure one channel is used per pixel # Also for some optimization # Snow. We do things funky here as alpha will be 1 already. if heightmap.getGrayVal(x, y) < 200: colormap.setAlpha(x, y, 0) else: colormap.setAlpha(x, y, 1) # Beach. Estimations from http://www.simtropolis.com/omnibus/index.cfm/Main.SimCity_4.Custom_Content.Custom_Terrains_and_Using_USGS_Data if heightmap.getGrayVal(x,y) < 62: colormap.setBlue(x, y, 1) # Rock elif slopemap.getGrayVal(x, y) > 170: colormap.setRed(x, y, 1) else: colormap.setGreen(x, y, 1) self.colorTexture = Texture() self.colorTexture.load(colormap) self.colorTS = TextureStage('color') self.colorTS.setSort(0) self.colorTS.setPriority(1) self.setSurfaceTextures() self.generateWater(2) taskMgr.add(self.updateTerrain, "updateTerrain") print "Done with terrain generation" messenger.send("finishedTerrainGen", [[self.xsize, self.ysize]]) self.terrain.getRoot().analyze() self.accept("h", self.switchWater) def regenerateWorld(self): '''Regenerates world, often upon city exit.''' self.terrain.generate() root = self.terrain.getRoot() root.reparentTo(render) self.terrain.setSz(100) messenger.send('makePickable', [root]) # Set multi texture # Source http://www.panda3d.org/phpbb2/viewtopic.php?t=4536 #self.generateSurfaceTextures() self.generateWaterMap() self.setSurfaceTextures() self.generateWater(2) print "Done with terrain regeneration" messenger.send("finishedTerrainGen", [[self.xsize, self.ysize]]) def generateWaterMap(self): ''' Iterate through every pix of color map. This will be very slow so until faster method is developed, use sparingly getXSize returns pixels length starting with 1, subtract 1 for obvious reasons We also slip in checking for the water card size, which should only change when the color map does ''' print "GenerateWaterMap" self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax = -1,0,-1,0 for x in range(0, self.heightmap.getXSize()-1): for y in range(0, self.heightmap.getYSize()-1): # Else if statements used to make sure one channel is used per pixel # Also for some optimization # Snow. We do things funky here as alpha will be 1 already. if self.heightmap.getGrayVal(x,y) < 62: # Water card dimensions here # Y axis flipped from texture space to world space if self.waterXMin == -1 or x < self.waterXMin: self.waterXMin = x if not self.waterYMax: self.waterYMax = y if y < self.waterYMax: self.waterYMax = y if x > self.waterXMax: self.waterXMax = x if y > self.waterYMin: self.waterYMin = y # Transform y coords self.waterYMin = self.size-64 - self.waterYMin self.waterYMax = self.size-64 - self.waterYMax def generateOwnerTexture(self, tiles, cities): '''Generates a simple colored texture to be applied to the city info region overlay. Due to different coordinate systems (terrain org bottom left, texture top left) some conversions are needed, Also creates and sends a citylabels dict for the region view ''' self.citymap = PNMImage(self.xsize, self.ysize) citylabels = cities scratch = {} # Setup for city labels for ident in cities: scratch[ident] = [] # conversion for y axis ycon = [] s = self.ysize - 1 for y in range(self.ysize): ycon.append(s) s -= 1 for ident, city in cities.items(): if ident not in self.citycolors: self.citycolors[ident] = VBase3D(random.random(), random.random(), random.random()) for tile in tiles: self.citymap.setXel(tile.coords[0], ycon[tile.coords[1]], self.citycolors[tile.cityid]) # Scratch for labeling if tile.cityid: scratch[tile.cityid].append((tile.coords[0], tile.coords[1])) for ident, values in scratch.items(): xsum = 0 ysum = 0 n = 0 for coords in values: xsum += coords[0] ysum += coords[1] n += 1 xavg = xsum/n yavg = ysum/n print "Elevation:", self.terrain.getElevation(xavg, yavg) z = self.terrain.getElevation(xavg, yavg)*100 citylabels[ident]["position"] = (xavg, yavg, z+15) print "Citylabels:", citylabels messenger.send("updateCityLabels", [citylabels, self.terrain]) def generateSurfaceTextures(self): # Textureize self.grassTexture = loader.loadTexture("Textures/grass.png") self.grassTS = TextureStage('grass') self.grassTS.setSort(1) self.rockTexture = loader.loadTexture("Textures/rock.jpg") self.rockTS = TextureStage('rock') self.rockTS.setSort(2) self.rockTS.setCombineRgb(TextureStage.CMAdd, TextureStage.CSLastSavedResult, TextureStage.COSrcColor, TextureStage.CSTexture, TextureStage.COSrcColor) self.sandTexture = loader.loadTexture("Textures/sand.jpg") self.sandTS = TextureStage('sand') self.sandTS.setSort(3) self.sandTS.setPriority(5) self.snowTexture = loader.loadTexture("Textures/ice.png") self.snowTS = TextureStage('snow') self.snowTS.setSort(4) self.snowTS.setPriority(0) # Grid for city placement and guide and stuff self.gridTexture = loader.loadTexture("Textures/grid.png") self.gridTexture.setWrapU(Texture.WMRepeat) self.gridTexture.setWrapV(Texture.WMRepeat) self.gridTS = TextureStage('grid') self.gridTS.setSort(5) self.gridTS.setPriority(10) def enterCity(self, ident, city, position, tiles): '''Identifies which terrain blocks city belogs to and disables those that are not A lot of uneeded for loops in here. Will need to find a better way later.''' #root = self.terrain.getRoot() children = [] for terrain in self.terrain.terrains: root = terrain.getRoot() children += root.getChildren() keepBlocks = [] # Reset water dimentions self.waterXMin = 0 self.waterXMax = 0 self.waterYMin = 0 self.waterYMax = 0 for tile in tiles: blockCoords = self.terrain.getBlockFromPos(tile.coords[0], tile.coords[1]) block = self.terrain.getBlockNodePath(blockCoords[0], blockCoords[1]) if block not in keepBlocks: keepBlocks.append(block) if self.heightmap.getGrayVal(tile.coords[0], self.size-tile.coords[1]) < 62: # Water card dimensions here # Y axis flipped from texture space to world space if not self.waterXMin: self.waterXMin = tile.coords[0] if not self.waterYMin: self.waterYMin = tile.coords[1] if tile.coords[1] > self.waterYMax: self.waterYMax = tile.coords[1] if tile.coords[0] > self.waterXMax: self.waterXMax = tile.coords[0] if tile.coords[0] < self.waterXMin: self.waterXMin = tile.coords[0] if tile.coords[1] < self.waterYMin: self.waterYMin = tile.coords[1] for child in children: if child not in keepBlocks: child.detachNode() self.view = ident self.generateWater(2) def newTerrainOverlay(self, task): root = self.terrain.getRoot() position = picker.getMouseCell() if position: # Check to make sure we do not go out of bounds if position[0] < 32: position = (32, position[1]) elif position[0] > self.xsize-32: position = (self.xsize-32, position[1]) if position[1] < 32: position = (position[0], 32) elif position [1] > self.ysize-32: position = (position[0], self.size-32) root.setTexOffset(self.tileTS, -(position[0]-32)/64, -(position[1]-32)/64) return task.cont def regionViewFound(self): '''Gui for founding a new city!''' self.setOwnerTextures() root = self.terrain.getRoot() task = taskMgr.add(self.newTerrainOverlay, "newTerrainOverlay") tileTexture = loader.loadTexture("Textures/tile.png") tileTexture.setWrapU(Texture.WMClamp) tileTexture.setWrapV(Texture.WMClamp) self.tileTS = TextureStage('tile') self.tileTS.setSort(6) self.tileTS.setMode(TextureStage.MDecal) #self.tileTS.setColor(Vec4(1,0,1,1)) root.setTexture(self.tileTS, tileTexture) root.setTexScale(self.tileTS, self.terrain.xchunks, self.terrain.ychunks) self.acceptOnce("mouse1", self.regionViewFound2) self.acceptOnce("escape", self.cancelRegionViewFound) def regionViewFound2(self): '''Grabs cell location for founding. The texture coordinate is used as the mouse may enter an out of bounds area. ''' root = self.terrain.getRoot() root_position = root.getTexOffset(self.tileTS) # We offset the position of the texture, so we will now put the origin of the new city not on mouse cursor but the "bottom left" of it. Just need to add 32 to get other edge position = [int(abs(root_position[0]*64)), int(abs(root_position[1]*64))] self.cancelRegionViewFound() messenger.send("found_city_name", [position]) def cancelRegionViewFound(self): taskMgr.remove("newTerrainOverlay") root = self.terrain.getRoot() root.clearTexture(self.tileTS) # Restore original mouse function self.accept("mouse1", self.lclick) messenger.send("showRegionGUI") def setSurfaceTextures(self): self.ownerview = False root = self.terrain.getRoot() root.clearTexture() #self.terrain.setTextureMap() root.setTexture( self.colorTS, self.colorTexture ) root.setTexture( self.grassTS, self.grassTexture ) root.setTexScale(self.grassTS, self.size/8, self.size/8) root.setTexture( self.rockTS, self.rockTexture ) root.setTexScale(self.rockTS, self.size/8, self.size/8) root.setTexture( self.sandTS, self.sandTexture) root.setTexScale(self.sandTS, self.size/8, self.size/8) root.setTexture( self.snowTS, self.snowTexture ) root.setTexScale(self.snowTS, self.size/8, self.size/8) root.setTexture( self.gridTS, self.gridTexture ) root.setTexScale(self.gridTS, self.xsize, self.ysize) root.setShaderInput('size', self.xsize, self.ysize, self.size, self.size) root.setShader(loader.loadShader('Shaders/terraintexture.sha')) def setOwnerTextures(self): self.ownerview = True root = self.terrain.getRoot() root.clearShader() root.clearTexture() cityTexture = Texture() cityTexture.load(self.citymap) cityTS = TextureStage('citymap') cityTS.setSort(0) root.setTexture( self.gridTS, self.gridTexture ) root.setTexScale(self.gridTS, self.terrain.xchunks, self.terrain.ychunks) root.setTexture(cityTS, cityTexture, 1) def updateRegion(self, heightmap, tiles, cities): self.generateOwnerTexture(tiles, cities) if self.ownerview: self.setOwnerTextures() def updateTerrain(self, task): '''Updates terrain and water''' self.terrain.update() # Water if self.waterType is 2: pos = base.camera.getPos() render.setShaderInput('time', task.time) mc = base.camera.getMat( ) self.water.changeCameraPos(pos,mc) self.water.changeCameraPos(pos,mc) #print "Render diagnostics" #render.analyze() #base.cTrav.showCollisions(render) return task.cont def generateWater(self, style): print "Generate Water:", self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax '''Generates water style 0: blue card style 1: reflective card style 2: reflective card with shaders ''' self.waterHeight = 22.0 if self.water: self.water.removeNode() if style is 0: cm = CardMaker("water") #cm.setFrame(-1, 1, -1, 1) cm.setFrame(self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax) cm.setColor(0, 0, 1, 0.9) self.water = render.attachNewNode(cm.generate()) if self.waterYMax > self.waterXMax: size = self.waterYMax else: size = self.waterXMax self.water.lookAt(0, 0, -1) self.water.setZ(self.waterHeight) messenger.send('makePickable', [self.water]) elif style is 1: # From Prosoft's super awesome terrain demo cm = CardMaker("water") #cm.setFrame(-1, 1, -1, 1) cm.setFrame(self.waterXMin, self.waterXMax, self.waterYMin, self.waterYMax) self.water = render.attachNewNode(cm.generate()) if self.waterYMax > self.waterXMax: size = self.waterYMax else: size = self.waterXMax #self.water.setScale(size) self.water.lookAt(0, 0, -1) self.water.setZ(self.waterHeight) self.water.setShaderOff(1) self.water.setLightOff(1) self.water.setAlphaScale(0.5) self.water.setTransparency(TransparencyAttrib.MAlpha) wbuffer = base.win.makeTextureBuffer("water", 512, 512) wbuffer.setClearColorActive(True) wbuffer.setClearColor(base.win.getClearColor()) self.wcamera = base.makeCamera(wbuffer) self.wcamera.reparentTo(render) self.wcamera.node().setLens(base.camLens) self.wcamera.node().setCameraMask(BitMask32.bit(1)) self.water.hide(BitMask32.bit(1)) wtexture = wbuffer.getTexture() wtexture.setWrapU(Texture.WMClamp) wtexture.setWrapV(Texture.WMClamp) wtexture.setMinfilter(Texture.FTLinearMipmapLinear) self.wplane = Plane(Vec3(0, 0, 1), Point3(0, 0, self.water.getZ())) wplanenp = render.attachNewNode(PlaneNode("water", self.wplane)) tmpnp = NodePath("StateInitializer") tmpnp.setClipPlane(wplanenp) tmpnp.setAttrib(CullFaceAttrib.makeReverse()) self.wcamera.node().setInitialState(tmpnp.getState()) self.water.projectTexture(TextureStage("reflection"), wtexture, self.wcamera) messenger.send('makePickable', [self.water]) elif style is 2: # From Clcheung just as super awesome demomaster self.water_level = Vec4(0.0, 0.0, self.waterHeight, 1.0) self.water = water.WaterNode(self.waterXMin, self.waterYMin, self.waterXMax, self.waterYMax, self.water_level.getZ()) self.water.setStandardControl() self.water.changeParams(None) wl=self.water_level wl.setZ(wl.getZ()-0.05) #root.setShaderInput('waterlevel', self.water_level) render.setShaderInput('time', 0) messenger.send('makePickable', [self.water.waterNP])
def generateNode(self, name, DB, parentNode): log.debug("Setting up " + name) bodyEntity = sandbox.createEntity() component = CelestialComponent() if DB['type'] == 'solid': body = Body(name) elif DB['type'] == 'moon': body = Body(name) body.kind = "moon" elif DB['type'] == 'star': body = Star(name) body.absoluteM = DB['absolute magnitude'] body.spectral = DB['spectral'] elif DB['type'] == 'barycenter': body = BaryCenter(name) component.kind = TYPES[DB['type']] if DB['type'] != "barycenter": component.mass = DB['mass'] body.radius = DB['radius'] body.rotation = DB['rotation'] if 'orbit' in DB: component.orbit = DB['orbit'] body.period = DB['period'] #body.setPos(self.get2DBodyPosition(component, universals.day)) component.truePos = self.get2DBodyPosition(component, universals.day) if name == "Earth": #universals.spawn = component.truePos + LPoint3d(0, 6671, 0) universals.spawn = component.truePos + LPoint3d(6671, 0, 0) if parentNode == universals.solarSystemRoot: universals.defaultSOIid = bodyEntity.id component.soi = 0 elif DB['type'] != 'star' or DB['type'] != 'barycenter': component.soi = self.getSOI(component.mass, self.bodies[0].mass, component.orbit['a']) body.type = DB['type'] body.reparentTo(parentNode) component.nodePath = body self.bodies.append(component) bodyEntity.addComponent(component) if universals.runClient and DB['type'] == 'star': component = graphicsComponents.RenderComponent() #component.mesh = NodePath(name) #self.sphere.copyTo(component.mesh) #component.mesh = shapeGenerator.Sphere(body.radius, 128, name) component.mesh = shapeGenerator.Sphere(body.radius, 64, name) #component.mesh.setScale(body.radius) component.mesh.reparentTo(sandbox.base.render) sandbox.send('makePickable', [component.mesh]) #texture = sandbox.base.loader.loadTexture('planets/' + DB['texture']) #texture.setMinfilter(Texture.FTLinearMipmapLinear) #ts1 = TextureStage('textures1') #ts1.setMode(TextureStage.MGlow) #component.mesh.setTexture(ts1, texture) #component.mesh.setTexture(texture, 1) component.light = component.mesh.attachNewNode(PointLight("sunPointLight")) component.light.node().setColor(Vec4(1, 1, 1, 1)) sandbox.base.render.setLight(component.light) bodyEntity.addComponent(component) #Shader test componentStar = graphicsComponents.StarRender() componentStar.noise_texture = Texture('noise') componentStar.noise_texture.setup2dTexture() img = PNMImage(1024, 1024) for y in range(1024): for x in range(1024): img.setXel(x, y, componentStar.noise.noise(x, y)) #print componentStar.noise.noise(x, y) componentStar.noise_texture.load(img) #componentStar.noise_texture.write('test.png') #component.mesh.setTexture(componentStar.noise_texture, 1) texture = sandbox.base.loader.loadTexture('planets/' + DB['texture']) ts1 = TextureStage('textures1') ts1.setMode(TextureStage.MGlow) component.mesh.setTexture(ts1, componentStar.noise_texture) #component.mesh.setTexture(ts1, texture) component.mesh.setShaderInput('time', universals.get_day_in_seconds()) shaders = Shader.load(Shader.SLGLSL, 'vortexVertex.glsl', 'starFrag.glsl') component.mesh.setShader(shaders) sandbox.send('makePickable', [component.mesh]) if universals.runClient and (DB['type'] == 'solid' or DB['type'] == 'moon'): component = graphicsComponents.RenderComponent() #component.mesh = shapeGenerator.Sphere(body.radius, 128, name) #component.mesh = shapeGenerator.Sphere(body.radius, 64, name) component.mesh = surface_mesh.make_planet(name=name, scale=body.radius) #sandbox.send('makePickable', [component.mesh]) sandbox.send('makePickable', [component.mesh.node_path]) #component.mesh.setScale(body.radius) component.mesh.reparent_to(sandbox.base.render) # Doing world text text = TextNode('node name') text.setText(name) #textNodePath = component.mesh.attachNewNode(text) textNodePath = component.mesh.node_path.attachNewNode(text) textNodePath.setScale(0.07) component.mesh.set_textures(DB['texture'], night_path=DB['night'], gloss_path=DB['spec']) component.mesh.set_ambient(1, 1, 1, 1) component.mesh.set_diffuse(1, 1, 1, 1) component.mesh.set_specular(1, 1, 1, 1) component.mesh.set_shininess(100) '''if '#' in DB['texture']: component.mesh.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition) component.mesh.setTexProjector(TextureStage.getDefault(), sandbox.base.render, component.mesh) component.mesh.setTexScale(TextureStage.getDefault(), 1, 1, -1) component.mesh.setTexHpr(TextureStage.getDefault(), 90, -18, 90) #self.mesh.setHpr(0, 90, 0) texture = loader.loadCubeMap('planets/' + DB['texture']) else: texture = sandbox.base.loader.loadTexture('planets/' + DB['texture']) #texture.setMinfilter(Texture.FTLinearMipmapLinear) component.mesh.setTexture(texture, 1)''' '''if "atmosphere" in DB: component.atmosphere = shapeGenerator.Sphere(-1, 128) component.atmosphere.reparentTo(render) component.atmosphere.setScale(body.radius * 1.025) outerRadius = component.atmosphere.getScale().getX() scale = 1 / (outerRadius - component.body.getScale().getX()) component.atmosphere.setShaderInput("fOuterRadius", outerRadius) component.atmosphere.setShaderInput("fInnerRadius", component.mesh.getScale().getX()) component.atmosphere.setShaderInput("fOuterRadius2", outerRadius * outerRadius) component.atmosphere.setShaderInput("fInnerRadius2", component.mesh.getScale().getX() * component.mesh.getScale().getX()) component.atmosphere.setShaderInput("fKr4PI", 0.000055 * 4 * 3.14159) component.atmosphere.setShaderInput("fKm4PI", 0.000015 * 4 * 3.14159) component.atmosphere.setShaderInput("fScale", scale) component.atmosphere.setShaderInput("fScaleDepth", 0.25) component.atmosphere.setShaderInput("fScaleOverScaleDepth", scale / 0.25) # Currently hard coded in shader component.atmosphere.setShaderInput("fSamples", 10.0) component.atmosphere.setShaderInput("nSamples", 10) # These do sunsets and sky colors # Brightness of sun ESun = 15 # Reyleight Scattering (Main sky colors) component.atmosphere.setShaderInput("fKrESun", 0.000055 * ESun) # Mie Scattering -- Haze and sun halos component.atmosphere.setShaderInput("fKmESun", 0.000015 * ESun) # Color of sun component.atmosphere.setShaderInput("v3InvWavelength", 1.0 / math.pow(0.650, 4), 1.0 / math.pow(0.570, 4), 1.0 / math.pow(0.465, 4)) #component.atmosphere.setShader(Shader.load("atmo.cg"))''' bodyEntity.addComponent(component) log.info(name + " set Up") if 'bodies' in DB: for bodyName, bodyDB in DB['bodies'].items(): self.generateNode(bodyName, bodyDB, body)
from panda3d.core import PNMImage, Vec3 lutSize = 32 image = PNMImage(lutSize * lutSize, lutSize, 3, 2**16 - 1) for r in xrange(lutSize): for g in xrange(lutSize): for b in xrange(lutSize): image.setXel(r + b * lutSize, g, r / float(lutSize), g / float(lutSize), b / float(lutSize)) image.write("Default.png")
class ColorPicker(NodePath): def __init__(self, parent, minSat, maxSat, minVal, maxVal, callback, pos=(0, 0, 0)): NodePath.__init__(self, 'colorPicker') self.reparentTo(parent) self.setPos(pos) self.minSat = minSat self.maxSat = maxSat self.minVal = minVal self.maxVal = maxVal self.callback = callback self.lastColor = None self.image = PNMImage(int((self.maxSat - self.minSat) * 100), int((self.maxVal - self.minVal) * 100)) self.slider = DirectSlider(self, relief=None, image='phase_3/maps/color_picker_hue.jpg', scale=0.3, pos=(0.2, 0, 0), image_scale=(0.1, 1.0, 1.0), pageSize=5, orientation=DGG.VERTICAL, command=self.__chooseHue) self.button = DirectButton(self, relief=None, image=None, scale=0.3, pos=(-0.2, 0, 0), frameColor=(1, 1, 1, 0.1), pressEffect=0) self.button.bind(DGG.B1PRESS, self.__startPick) self.button.bind(DGG.B1RELEASE, self.__stopPick) self.__chooseHue() def uniqueName(self, name): return 'ColorPicker-%s-%s' % (id(self), name) def removeNode(self): NodePath.removeNode(self) self.destroy() def destroy(self): if not self.slider: return self.__stopPick() self.slider.destroy() self.button.destroy() self.slider = None self.button = None self.image = None def __calcRelative(self, value, baseMin, baseMax, limitMin, limitMax): return ((limitMax - limitMin) * (value - baseMin) / (baseMax - baseMin)) + limitMin def __chooseHue(self): for x in xrange(self.image.getXSize()): for y in xrange(self.image.getYSize()): self.image.setXel( x, y, colorsys.hsv_to_rgb(self.slider['value'], (x / 100.0) + self.minSat, (y / 100.0) + self.minVal)) texture = Texture() texture.load(self.image) self.button['image'] = texture def __pickColor(self, task): x = base.mouseWatcherNode.getMouseX() y = base.mouseWatcherNode.getMouseY() win_w, win_h = base.win.getSize() if win_w < win_h: y *= 1. * win_h / win_w else: x *= 1. * win_w / win_h x -= self.button.getX(aspect2d) y -= self.button.getZ(aspect2d) image_scale = self.button['image_scale'] x = (.5 + x / (2. * self.button.getSx(aspect2d) * image_scale[0])) y = (.5 + y / -(2. * self.button.getSz(aspect2d) * image_scale[2])) if not (0.0 <= x <= 1.0 and 0.0 <= y <= 1.0): return task.cont x = self.__calcRelative(x, 0.0, 1.0, self.minSat, self.maxSat) y = self.__calcRelative(y, 0.0, 1.0, self.minVal, self.maxVal) rgb = colorsys.hsv_to_rgb(self.slider['value'], x, y) + (1, ) rgb = tuple([float('%.2f' % x) for x in rgb]) if self.lastColor != rgb: self.callback(rgb) self.lastColor = rgb return task.cont def __startPick(self, extra=None): self.__stopPick() taskMgr.add(self.__pickColor, self.uniqueName('colorDragTask')) def __stopPick(self, extra=None): taskMgr.remove(self.uniqueName('colorDragTask'))