def _createMapTextureCard(self): mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION) mapImage.fill(*self._bgColor) fgColor = VBase4D(*self._fgColor) for x in range(self._mazeHeight): for y in range(self._mazeWidth): if self._mazeCollTable[y][x] == 1: ax = float(x) / self._mazeWidth * MAP_RESOLUTION invertedY = self._mazeHeight - 1 - y ay = float(invertedY) / self._mazeHeight * MAP_RESOLUTION self._drawSquare(mapImage, int(ax), int(ay), 10, fgColor) mapTexture = Texture('mapTexture') mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) mapTexture.setMinfilter(Texture.FTLinear) mapTexture.load(mapImage) mapTexture.setWrapU(Texture.WMClamp) mapTexture.setWrapV(Texture.WMClamp) mapImage.clear() del mapImage cm = CardMaker('map_cardMaker') cm.setFrame(-1.0, 1.0, -1.0, 1.0) map = self.attachNewNode(cm.generate()) map.setTexture(mapTexture, 1) return map
def setupBackgroundImage(self): image_file = Filename(TestGameBase.__BACKGROUND_IMAGE_PATH__) # check if image can be loaded img_head = PNMImageHeader() if not img_head.readHeader(image_file ): raise IOError("PNMImageHeader could not read file %s. Try using absolute filepaths"%(image_file.c_str())) sys.exit() # Load the image with a PNMImage w = img_head.getXSize() h = img_head.getYSize() img = PNMImage(w,h) #img.alphaFill(0) img.read(image_file) texture = Texture() texture.setXSize(w) texture.setYSize(h) texture.setZSize(1) texture.load(img) texture.setWrapU(Texture.WM_border_color) # gets rid of odd black edges around image texture.setWrapV(Texture.WM_border_color) texture.setBorderColor(LColor(0,0,0,0)) # creating CardMaker to hold the texture cm = CardMaker('background') cm.setFrame(-0.5*w,0.5*w,-0.5*h,0.5*h) # This configuration places the image's topleft corner at the origin (left, right, bottom, top) background_np = NodePath(cm.generate()) background_np.setTexture(texture) background_np.reparentTo(self.render) background_np.setPos(TestGameBase.__BACKGROUND_POSITION__) background_np.setScale(TestGameBase.__BACKGROUND_SCALE__)
def _createMapTextureCard(self): mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION) mapImage.fill(*self._bgColor) fgColor = VBase4D(*self._fgColor) for x in xrange(self._mazeHeight): for y in xrange(self._mazeWidth): if self._mazeCollTable[y][x] == 1: ax = float(x) / self._mazeWidth * MAP_RESOLUTION invertedY = self._mazeHeight - 1 - y ay = float(invertedY) / self._mazeHeight * MAP_RESOLUTION self._drawSquare(mapImage, int(ax), int(ay), 10, fgColor) mapTexture = Texture('mapTexture') mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) mapTexture.setMinfilter(Texture.FTLinear) mapTexture.load(mapImage) mapTexture.setWrapU(Texture.WMClamp) mapTexture.setWrapV(Texture.WMClamp) mapImage.clear() del mapImage cm = CardMaker('map_cardMaker') cm.setFrame(-1.0, 1.0, -1.0, 1.0) map = self.attachNewNode(cm.generate()) map.setTexture(mapTexture, 1) return map
def makeTextureMap(self): '''Citymania function that generates and sets the 4 channel texture map''' self.colorTextures = [] for terrain in self.terrains: terrain.getRoot().clearTexture() heightmap = terrain.heightfield() colormap = PNMImage(heightmap.getXSize()-1, heightmap.getYSize()-1) colormap.addAlpha() slopemap = 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) colorTexture = Texture() colorTexture.load(colormap) colorTS = TextureStage('color') colorTS.setSort(0) colorTS.setPriority(1) self.colorTextures.append((colorTexture, colorTS))
def __init__(self): load_prc_file_data("", """ textures-power-2 none window-type offscreen win-size 100 100 gl-coordinate-system default notify-level-display error print-pipe-types #f """) ShowBase.__init__(self) dest_tex = Texture() dest_tex.setup_2d_texture(2048, 2048, Texture.T_unsigned_byte, Texture.F_rgba8) cshader = Shader.load_compute(Shader.SL_GLSL, "grain.compute.glsl") node = NodePath("") node.set_shader(cshader) node.set_shader_input("DestTex", dest_tex) attr = node.get_attrib(ShaderAttrib) self.graphicsEngine.dispatch_compute( (2048 // 16, 2048 // 16, 1), attr, self.win.get_gsg()) base.graphicsEngine.extract_texture_data(dest_tex, base.win.get_gsg()) # Convert to single channel img = PNMImage(2048, 2048, 1, 255) dest_tex.store(img) img.set_num_channels(1) tex = Texture() tex.load(img) tex.write("grain.txo.pz")
class VideoWall(object): res = 256 def __init__(self, loader, parentNodePath): w = loader.loadModel("plane") w.reparentTo(parentNodePath) size = 6 w.setPos(3.5, 15, size / 2 - 3) w.setColor(1,0,0) w.setHpr(0, 180, 0) w.setScale(size, 1, size / 1.33) w.setTwoSided(True) self.tx = Texture("video") self.tx.setup2dTexture(self.res, self.res, Texture.TUnsignedByte, Texture.FRgb8) # this makes some important setup call self.tx.load(PNMImage(self.res, self.res)) w.setTexture(self.tx) m = Material("vid") m.setTwoside(True) m.setEmission(VBase4(1,1,1,1)) w.setMaterial(m) w.setFogOff() def updateFromPixbuf(self, pb): scaled = pb.scale_simple(self.res, self.res, gtk.gdk.INTERP_BILINEAR) # about 3ms n = numpy.fromstring(scaled.get_pixels(), dtype=numpy.uint8).reshape((-1,3)) flipped = numpy.fliplr(n).tostring() self.tx.setRamImage(flipped)
def __init__(self): load_prc_file_data( "", """ textures-power-2 none window-type offscreen win-size 100 100 gl-coordinate-system default notify-level-display error print-pipe-types #f """) ShowBase.__init__(self) dest_tex = Texture() dest_tex.setup_2d_texture(2048, 2048, Texture.T_unsigned_byte, Texture.F_rgba8) cshader = Shader.load_compute(Shader.SL_GLSL, "grain.compute.glsl") node = NodePath("") node.set_shader(cshader) node.set_shader_input("DestTex", dest_tex) attr = node.get_attrib(ShaderAttrib) self.graphicsEngine.dispatch_compute((2048 // 16, 2048 // 16, 1), attr, self.win.get_gsg()) base.graphicsEngine.extract_texture_data(dest_tex, base.win.get_gsg()) # Convert to single channel img = PNMImage(2048, 2048, 1, 255) dest_tex.store(img) img.set_num_channels(1) tex = Texture() tex.load(img) tex.write("grain.txo.pz")
def makeTextureMap(self): '''Citymania function that generates and sets the 4 channel texture map''' self.colorTextures = [] for terrain in self.terrains: terrain.getRoot().clearTexture() heightmap = terrain.heightfield() colormap = PNMImage(heightmap.getXSize() - 1, heightmap.getYSize() - 1) colormap.addAlpha() slopemap = 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) colorTexture = Texture() colorTexture.load(colormap) colorTS = TextureStage('color') colorTS.setSort(0) colorTS.setPriority(1) self.colorTextures.append((colorTexture, colorTS))
class MatPlotLibDemo(ShowBase): def __init__(self): ShowBase.__init__(self) base.setFrameRateMeter(True) m = loader.loadModel("models/smiley") m.reparent_to(render) m.set_pos(0, 5, 0) x_size, y_size = 640, 480 xy_ratio = float(y_size) / float(x_size) self.plot = Plot(x_size, y_size) self.input_img = PNMImage(x_size, y_size) self.input_tex = Texture() self.input_tex.load(self.input_img) self.card = CardMaker('pygame_card') self.card.setUvRange(Point2(0, 1), # ll Point2(1, 1), # lr Point2(1, 0), # ur Point2(0, 0)) # ul self.screen = render.attach_new_node(self.card.generate()) self.screen.set_scale(1, 1, xy_ratio) self.screen.set_pos(-0.5, 2, -0.5 * xy_ratio) self.screen.setTexture(self.input_tex) # FIXME: Apparently mpl's print_to_buffer() doesn't write # alpha values properly. self.screen.setTransparency(TransparencyAttrib.MAlpha) taskMgr.add(self.update, "update plot") def update(self, task): self.input_tex.set_ram_image_as(self.plot.draw(), "RGBA") return task.cont
def to_textures( images: list, name_mask: str = None, image_sizes: LPoint2 = None, texture_filter: SamplerState = None, ) -> list: """Convert provided list of PNMImage objects into Texture objects""" # doing it like that to enable ez override in get_textures() name_mask = name_mask or "sprite" textures = [] # without name mask, this may seem like it returns empty sequence, but its not for num, item in enumerate(images): # this is how we turn image into texture texture = Texture(f"{name_mask}_{num}") texture.load(item) if texture_filter is not None: texture.set_magfilter(texture_filter) texture.set_minfilter(texture_filter) if image_sizes: texture.set_orig_file_size(*image_sizes, 1) textures.append(texture) log.debug(f"Got following textures: {textures}") return textures
def cardmaker_debug(self): for node in render2d.find_all_matches("pfm"): node.remove_node() for text in base.a2dBottomLeft.find_all_matches("*"): text.remove_node() width = 0.2 # render2d coordinates range: [-1..1] # Pseudo-normalize our PfmFile for better contrast. normalized_pfm = PfmFile(self.RotorPFM) max_p = LVector3() normalized_pfm.calc_min_max(LVector3(), max_p) normalized_pfm *= 1.0 / max_p.x # Put it in a texture tex = Texture() tex.load(normalized_pfm) # Apply the texture to a quad and put it in the lower left corner. cm = CardMaker("pfm") cm.set_frame(0, width, 0, width / normalized_pfm.get_x_size() * normalized_pfm.get_y_size()) card = base.render2d.attach_new_node(cm.generate()) card.set_pos(-1, 0, -1) card.set_texture(tex) # Display max value text self.genLabelText(-3, "Max value: {:.3f} == {:.2f}m".format(max_p.x, max_p.x * self.terrain_scale.z), parent="a2dBottomLeft")
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 __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 _load_noise_tex(self): """ Loads the default 4x4 noise tex """ random.seed(42) img = PNMImage(4, 4, 3) for x in range(16): img.set_xel(x%4, x//4, random.random(), random.random(), random.random()) tex = Texture("Random4x4") tex.load(img) self._pipeline.stage_mgr.add_input("Noise4x4", tex)
def new(size, color=(255,255,255)): img = PNMImage(*size) if len(color) == 4: img.addAlpha() img.fill(*color) panda_tex = PandaTexture('texture') panda_tex.load(img) return Texture(panda_tex)
def create_noise_textures(self): # Always generate the same random textures seed(42) img = PNMImage(4, 4, 3) for x in range(4): for y in range(4): img.set_xel(x, y, random(), random(), random()) tex = Texture("Rand4x4") tex.load(img) self._target.set_shader_input("Noise4x4", tex)
def __apply_Textures(self, recipe, tex_dict): for i, ter_dict in enumerate(recipe['terrains']): tex_img = PNMImage() tex_img.read(Filename("{}/tex/{}".format(recipe['planet_path'], ter_dict['texture']))) tex = Texture() tex.load(tex_img) tex.setMinfilter(Texture.FTLinear) ts = TextureStage(str(i)) ts.setSort(i) self.NP.setTexture(ts, tex, i*10)
def getTextureFromImage(pnmImage): print("myImage.getNumChannels(): ", pnmImage.getNumChannels()) print("myImage.getXSize(): ", pnmImage.getXSize()) print("myImage.getYSize(): ", pnmImage.getYSize()) print("myImage.hasAlpha(): ", pnmImage.hasAlpha()) # assign the PNMImage to a Texture (load PNMImage to Texture, opposite of store) myTexture = Texture() myTexture.load(pnmImage) return myTexture
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 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 __get_Texture(self, ref_img): # Convert ref_img into texture. with TimeIt() as prep_timer: ref_tex = Texture() # Ensure ref image has an alpha channel. if not ref_img.hasAlpha(): ref_img.addAlpha() ref_img.alphaFill(1.0) # Load tex and set format ref_tex.load(ref_img) ref_tex.setFormat(self.img_format) self.prepare_time += round(prep_timer.total_time, 3) return ref_tex
def __Call(self, func_name, **kwargs): """Receive python call and redirect request to relevant function in image shader library; return modified image.""" # Copy self.__Lines (need to keep orig for further calls) and # add "#define" statement to top to trigger compilation of # relevant "#ifdef/def" function block in shader. lines = list(self.__LINES) lines.insert(2, "#define {}".format(func_name)) # Assemble lines into shader str and compile. shader_str = "".join(lines) self.__NP.setShader(Shader.makeCompute(Shader.SL_GLSL, shader_str)) # Set block size from workgroup size. block_x = int(self.x_size/self.workgroup_size.x) block_y = int(self.y_size/self.workgroup_size.y) block_z = int(self.z_size/self.workgroup_size.z) block_size = LVector3i(block_x,block_y,block_z) # Create mod_tex for GPU. with TimeIt() as prep_timer: mod_img = PNMImage(self.x_size, self.y_size, 4) mod_tex = Texture() mod_tex.load(mod_img) mod_tex.setMinfilter(Texture.FTLinear) mod_tex.setFormat(self.img_format) self.prepare_time += prep_timer.total_time # Pass textures to shader. self.__NP.setShaderInput("ref_tex", self.__ref_tex) self.__NP.setShaderInput("mod_tex", mod_tex) # Set any additional required inputs for this function. for input_name, input_val in list(kwargs.items()): if type(input_val) == PNMImage: input_val = self.__get_Texture(input_val) self.__NP.setShaderInput(input_name, input_val) # Call function in shader library. shader_attrib = self.__NP.getAttrib(ShaderAttrib) with TimeIt() as proc_timer: base.graphicsEngine.dispatch_compute(block_size, shader_attrib, self.__gsg) self.process_time += proc_timer.total_time # Extract modified texture from GPU. with TimeIt() as extract_timer: base.graphicsEngine.extractTextureData(mod_tex, self.__gsg) self.extract_time += extract_timer.total_time return mod_tex
def make_star(name='star', scale=1, color=Vec3(1), texture_size=64, debug=False): card_maker = CardMaker(name) card_maker.set_frame(-1, 1, -1, 1) node_path = NodePath(name) node = card_maker.generate() final_node_path = node_path.attach_new_node(node) final_node_path.set_billboard_point_eye() from panda3d.core import Filename shaders = Shader.load(Shader.SL_GLSL, Filename('Shader/Star/vertex.glsl'), Filename('Shader/Star/fragment.glsl'), Filename(''), Filename(''), Filename('')) if not shaders: print("WARNING. STAR SHADER FAILED TO LOAD", type(shaders)) else: final_node_path.set_shader_input('cameraSpherePos', 1, 1, 1) final_node_path.set_shader_input('sphereRadius', 1.0) final_node_path.set_shader_input('myCamera', base.camera) final_node_path.set_shader(shaders) final_node_path.set_shader_input('blackbody', color) material = Material() material.set_emission(VBase4(color, 1.0)) final_node_path.set_material(material) xn = PerlinNoise3(0.5, 0.5, 0.5) #yn = PerlinNoise3(0.5, 0.5, 0.5) texture = Texture('star') texture.setup_3d_texture() for z in range(texture_size): p = PNMImage(texture_size, texture_size) for y in range(texture_size): for x in range(texture_size): p.set_gray(x, y, abs(xn.noise(x, y, z))) texture.load(p, z, 0) diffuse = texture diffuse.setMinfilter(Texture.FTLinearMipmapLinear) diffuse.setAnisotropicDegree(4) final_node_path.set_texture(diffuse) normal = sandbox.base.loader.loadTexture('data/empty_textures/empty_normal.png') normalts = TextureStage('normalts') final_node_path.set_texture(normalts, normal) specular = sandbox.base.loader.loadTexture('data/empty_textures/empty_specular.png') spects = TextureStage('spects') final_node_path.set_texture(spects, specular) roughness = sandbox.base.loader.loadTexture('data/empty_textures/empty_roughness.png') roughts= TextureStage('roughts') final_node_path.set_texture(roughts, roughness) return final_node_path
def create_texture(image: Image) -> Texture: """Create a Panda3D Texture from a PIL Image.""" bytes_io = BytesIO() image.save(bytes_io, format='PNG') bytes_io.seek(0) stream = StringStream() stream.set_data(bytes_io.read()) bytes_io.close() pnm_image = PNMImage() pnm_image.read(stream) stream.clear_data() texture = Texture() texture.load(pnm_image) pnm_image.clear() return texture
def apply(self, shape, owner): if self.texture is None: if self.image is None: self.image = self.sprite.generate() texture = Texture() texture.load(self.image) self.texture = TransparentTexture(DirectTextureSource(texture), blend=TransparencyBlend.TB_PremultipliedAlpha) self.texture.set_tex_matrix(False) shape.instance.setTexGen(TextureStage.getDefault(), TexGenAttrib.MPointSprite) self.texture.apply(shape) shape.instance.set_depth_write(False) if self.background is not None: shape.instance.setBin('background', settings.deep_space_depth) owner.shader.apply(shape, self) shape.instance_ready = True
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 do_load_texture_array(self, textures): tex = Texture() tex.setup_2d_texture_array(len(textures)) for (page, texture) in enumerate(textures): filename = texture.source.texture_filename(None) if filename is not None: panda_filename = Filename.from_os_specific(filename) tex.read(fullpath=panda_filename, z=page, n=0, read_pages=False, read_mipmaps=False) else: print("Could not find", texture.source.texture_name(None)) image = texture.create_default_image() tex.load(image, z=page, n=0) return tex
class GenPointSprite(PointObject): def __init__(self, size=64): self.size = size self.half_size = size / 2.0 self.texture = None self.image = None def generate(self): return None def apply(self, instance): if self.texture is None: if self.image is None: self.image = self.generate() self.texture = Texture() self.texture.load(self.image) instance.setTexGen(TextureStage.getDefault(), TexGenAttrib.MPointSprite) instance.setTransparency(TransparencyAttrib.MAlpha, 1) instance.setTexture(TextureStage('ts'), self.texture, 1)
def make_star(name='star', scale=1, color=Vec3(1), texture_size=64, debug=False): card_maker = CardMaker(name) card_maker.set_frame(-1, 1, -1, 1) node_path = NodePath(name) node = card_maker.generate() final_node_path = node_path.attach_new_node(node) final_node_path.set_billboard_point_eye() shaders = Shader.load(Shader.SLGLSL, 'Shader/Star/vertex.glsl', 'Shader/Star/fragment.glsl') final_node_path.set_shader_input(b'cameraSpherePos', 1, 1, 1) final_node_path.set_shader_input(b'sphereRadius', 1.0) final_node_path.set_shader_input(b'myCamera', base.camera) final_node_path.set_shader(shaders) final_node_path.set_shader_input(b'blackbody', color) material = Material() material.set_emission(VBase4(color, 1.0)) final_node_path.set_material(material) xn = PerlinNoise3(0.5, 0.5, 0.5) #yn = PerlinNoise3(0.5, 0.5, 0.5) texture = Texture('star') texture.setup_3d_texture() for z in range(texture_size): p = PNMImage(texture_size, texture_size) for y in range(texture_size): for x in range(texture_size): p.set_gray(x, y, abs(xn.noise(x, y, z))) texture.load(p, z, 0) diffuse = texture diffuse.setMinfilter(Texture.FTLinearMipmapLinear) diffuse.setAnisotropicDegree(4) final_node_path.set_texture(diffuse) normal = base.loader.loadTexture('Data/Textures/EmptyNormalTexture.png') normalts = TextureStage('normalts') final_node_path.set_texture(normalts, normal) specular = base.loader.loadTexture('Data/Textures/EmptySpecularTexture.png') spects = TextureStage('spects') final_node_path.set_texture(spects, specular) roughness = base.loader.loadTexture('Data/Textures/EmptyRoughnessTexture.png') roughts= TextureStage('roughts') final_node_path.set_texture(roughts, roughness) return final_node_path
class TerrainNormalMap(TextureSource): cached = True def __init__(self, terrain, radius, height_scale): TextureSource.__init__(self) self.terrain = terrain self.loaded = False self.texture = None perimeter = 2.0 * pi * radius self.x_scale = (terrain.width / perimeter) * height_scale self.y_scale = (terrain.height / perimeter) * height_scale def load(self, patch, grayscale=False): if not self.loaded: image = self.terrain.make_terrain_normal(self.x_scale, self.y_scale) self.texture = Texture() self.texture.load(image) self.loaded = True return (self.texture, 0, 0)
def textureFromData(image_data, filename=""): tex = None if image_data: myTexture = Texture() myImage = PNMImage() success = myImage.read(StringStream(image_data), filename) if success == 1: #PNMImage can handle most texture formats myTexture.load(myImage) else: #Except for DDS, which PNMImage.read will return 0, so try to load as DDS success = myTexture.readDds(StringStream(image_data)) if success != 0: tex = myTexture tex.setMinfilter(Texture.FTLinearMipmapLinear) return tex
class PygameCard(ShowBase): def __init__(self): ShowBase.__init__(self) self.game = Game() self.camLens.setFov(120) self.input_img = PNMImage(self.game.res[0], self.game.res[1]) self.input_tex = Texture() self.input_tex.load(self.input_img) self.card = CardMaker('pygame_card') self.screen = render.attach_new_node(self.card.generate()) self.screen.setPos(-0.5,2,-0.5) self.screen.setTexture(self.input_tex) self.game_ram_image = self.game.surface.get_view("0") taskMgr.add(self.update, "update pygame_card") def update(self, task): self.game.update() self.input_tex.set_ram_image_as(self.game_ram_image, "RGBA") return task.cont
class CameraTexture: def __init__(self, pipe=PIPE_FILE): self.tex = Texture("CameraTexture") self.pipe = pipe self.redis = Redis() self.redis.delete("camera-lock") self.image = None self.thread = thread.start_new_thread(self.readImage, ()) def readImage(self): while True: with self.redis.lock("camera-lock"): newImage = PNMImage(self.pipe) self.image = newImage time.sleep(0.06) def update(self): if self.image: self.tex.load(self.image) def getTexture(self): return self.tex
class PygameCard(ShowBase): def __init__(self): ShowBase.__init__(self) self.game = Game() self.camLens.setFov(120) self.input_img = PNMImage(self.game.res[0], self.game.res[1]) self.input_tex = Texture() self.input_tex.load(self.input_img) self.card = CardMaker('pygame_card') self.screen = render.attach_new_node(self.card.generate()) self.screen.setPos(-0.5, 2, -0.5) self.screen.setTexture(self.input_tex) self.game_ram_image = self.game.surface.get_view("0") taskMgr.add(self.update, "update pygame_card") def update(self, task): self.game.update() self.input_tex.set_ram_image_as(self.game_ram_image, "RGBA") return task.cont
class VideoWall(object): res = 256 def __init__(self, loader, parentNodePath): w = loader.loadModel("plane") w.reparentTo(parentNodePath) size = 6 w.setPos(3.5, 15, size / 2 - 3) w.setColor(1, 0, 0) w.setHpr(0, 180, 0) w.setScale(size, 1, size / 1.33) w.setTwoSided(True) self.tx = Texture("video") self.tx.setup2dTexture(self.res, self.res, Texture.TUnsignedByte, Texture.FRgb8) # this makes some important setup call self.tx.load(PNMImage(self.res, self.res)) w.setTexture(self.tx) m = Material("vid") m.setTwoside(True) m.setEmission(VBase4(1, 1, 1, 1)) w.setMaterial(m) w.setFogOff() def updateFromPixbuf(self, pb): scaled = pb.scale_simple(self.res, self.res, gtk.gdk.INTERP_BILINEAR) # about 3ms n = numpy.fromstring(scaled.get_pixels(), dtype=numpy.uint8).reshape( (-1, 3)) flipped = numpy.fliplr(n).tostring() self.tx.setRamImage(flipped)
def createSequenceNode(self,name,img,cols,rows,scale_x,scale_y,frame_rate): seq = SequenceNode(name) w = int(img.getXSize()/cols) h = int(img.getYSize()/rows) counter = 0 for i in range(0,cols): for j in range(0,rows): sub_img = PNMImage(w,h) sub_img.addAlpha() sub_img.alphaFill(0) sub_img.fill(1,1,1) sub_img.copySubImage(img ,0 ,0 ,i*w ,j*h ,w ,h) # Load the image onto the texture texture = Texture() texture.setXSize(w) texture.setYSize(h) texture.setZSize(1) texture.load(sub_img) texture.setWrapU(Texture.WM_border_color) # gets rid of odd black edges around image texture.setWrapV(Texture.WM_border_color) texture.setBorderColor(LColor(0,0,0,0)) cm = CardMaker(name + '_' + str(counter)) cm.setFrame(-0.5*scale_x,0.5*scale_x,-0.5*scale_y,0.5*scale_y) card = NodePath(cm.generate()) seq.addChild(card.node(),counter) card.setTexture(texture) sub_img.clear() counter+=1 seq.setFrameRate(frame_rate) print "Sequence Node %s contains %i frames of size %s"%(name,seq.getNumFrames(),str((w,h))) return seq
class MatPlotLibDemo(ShowBase): def __init__(self): ShowBase.__init__(self) base.setFrameRateMeter(True) m = loader.loadModel("models/smiley") m.reparent_to(render) m.set_pos(0, 5, 0) x_size, y_size = 640, 480 xy_ratio = float(y_size) / float(x_size) self.plot = Plot(x_size, y_size) self.input_img = PNMImage(x_size, y_size) self.input_tex = Texture() self.input_tex.load(self.input_img) self.card = CardMaker('pygame_card') self.card.setUvRange( Point2(0, 1), # ll Point2(1, 1), # lr Point2(1, 0), # ur Point2(0, 0)) # ul self.screen = render.attach_new_node(self.card.generate()) self.screen.set_scale(1, 1, xy_ratio) self.screen.set_pos(-0.5, 2, -0.5 * xy_ratio) self.screen.setTexture(self.input_tex) # FIXME: Apparently mpl's print_to_buffer() doesn't write # alpha values properly. self.screen.setTransparency(TransparencyAttrib.MAlpha) taskMgr.add(self.update, "update plot") def update(self, task): self.input_tex.set_ram_image_as(self.plot.draw(), "RGBA") return task.cont
def mapSelection(self, maps): """ Generate a window with list and thumbnails of region maps availiable. Provide two options, load map or, if there is no local version, save local version """ self.mapDialog = DirectWindow(title = "Select Map") mapList = [] m = [""] import base64 for mapName in maps: heightmap = maps[mapName] image = PNMImage() image.read(StringStream(heightmap)) thumbnail = PNMImage(64, 64) thumbnail.gaussianFilterFrom(1, image) heightTexture = Texture() heightTexture.load(image) label = DirectRadioButton(text=mapName, image=heightTexture, variable=m, value=[mapName]) mapList.append(label) for button in mapList: button.setOthers(mapList) self.mapDialog.addScrolledList(mapList) okButton = DirectButton(text = self.getText('TXT_UI_OK'), command = self.selectMap, extraArgs=m) self.mapDialog.addVertical([okButton])
class Typist(object): TARGETS = { 'paper': { 'model': 'paper', 'textureRoot': 'Front', 'scale': Point3(0.85, 0.85, 1), 'hpr' : Point3(0, 0, 0), } } def __init__(self, base, typewriterNP, underDeskClip, sounds): self.base = base self.sounds = sounds self.underDeskClip = underDeskClip self.typeIndex = 0 self.typewriterNP = typewriterNP self.rollerAssemblyNP = typewriterNP.find("**/roller assembly") assert self.rollerAssemblyNP self.rollerNP = typewriterNP.find("**/roller") assert self.rollerNP self.carriageNP = typewriterNP.find("**/carriage") assert self.carriageNP self.baseCarriagePos = self.carriageNP.getPos() self.carriageBounds = self.carriageNP.getTightBounds() self.font = base.loader.loadFont('Harting.ttf', pointSize=32) self.pnmFont = PNMTextMaker(self.font) self.fontCharSize, _, _ = fonts.measureFont(self.pnmFont, 32) print "font char size: ",self.fontCharSize self.pixelsPerLine = int(round(self.pnmFont.getLineHeight())) self.target = None """ panda3d.core.NodePath """ self.targetRoot = None """ panda3d.core.NodePath """ self.paperY = 0.0 """ range from 0 to 1 """ self.paperX = 0.0 """ range from 0 to 1 """ self.createRollerBase() self.tex = None self.texImage = None self.setupTexture() self.scheduler = Scheduler() task = self.base.taskMgr.add(self.tick, 'timerTask') task.setDelay(0.01) def tick(self, task): self.scheduler.tick(globalClock.getRealTime()) return task.cont def setupTexture(self): """ This is the overlay/decal/etc. which contains the typed characters. The texture size and the font size are currently tied together. :return: """ self.texImage = PNMImage(1024, 1024) self.texImage.addAlpha() self.texImage.fill(1.0) self.texImage.alphaFill(1.0) self.tex = Texture('typing') self.tex.setMagfilter(Texture.FTLinear) self.tex.setMinfilter(Texture.FTLinear) self.typingStage = TextureStage('typing') self.typingStage.setMode(TextureStage.MModulate) self.tex.load(self.texImage) # ensure we can quickly update subimages self.tex.setKeepRamImage(True) # temp for drawing chars self.chImage = PNMImage(*self.fontCharSize) def drawCharacter(self, ch, px, py): """ Draw a character onto the texture :param ch: :param px: paperX :param py: paperY :return: the paper-relative size of the character """ h = self.fontCharSize[1] if ch != ' ': # position -> pixel, applying margins x = int(self.tex.getXSize() * (px * 0.8 + 0.1)) y = int(self.tex.getYSize() * (py * 0.8 + 0.1)) # always draw onto the paper, to capture # incremental character overstrikes self.pnmFont.generateInto(ch, self.texImage, x, y) if False: #print ch,"to",x,y,"w=",g.getWidth() self.tex.load(self.texImage) else: # copy an area (presumably) encompassing the character g = self.pnmFont.getGlyph(ord(ch)) cx, cy = self.fontCharSize # a glyph is minimally sized and "moves around" in its text box # (think ' vs. ,), so it has been drawn somewhere relative to # the 'x' and 'y' we wanted. x += g.getLeft() y -= g.getTop() self.chImage.copySubImage( self.texImage, 0, 0, # from x, y, # to cx, cy # size ) self.tex.loadSubImage(self.chImage, x, y) # toggle for a typewriter that uses non-proportional spacing #w = self.paperCharWidth(g.getWidth()) w = self.paperCharWidth() else: w = self.paperCharWidth() return w, h def start(self): self.target = None self.setTarget('paper') self.hookKeyboard() def createRollerBase(self): """ The paper moves such that it is tangent to the roller. This nodepath keeps a coordinate space relative to that, so that the paper can be positioned from (0,0,0) to (0,0,1) to "roll" it along the roller. """ bb = self.rollerNP.getTightBounds() #self.rollerNP.showTightBounds() self.paperRollerBase = self.rollerAssemblyNP.attachNewNode('rollerBase') self.paperRollerBase.setHpr(0, -20, 0) print "roller:",bb rad = abs(bb[0].y - bb[1].y) / 2 center = Vec3(-(bb[0].x+bb[1].x)/2 - 0.03, (bb[0].y-bb[1].y)/2, (bb[0].z+bb[1].z)/2) self.paperRollerBase.setPos(center) def setTarget(self, name): if self.target: self.target.removeNode() # load and transform the model target = self.TARGETS[name] self.target = self.base.loader.loadModel(target['model']) #self.target.setScale(target['scale']) self.target.setHpr(target['hpr']) # put it in the world self.target.reparentTo(self.paperRollerBase) rbb = self.rollerNP.getTightBounds() tbb = self.target.getTightBounds() rs = (rbb[1] - rbb[0]) ts = (tbb[1] - tbb[0]) self.target.setScale(rs.x / ts.x, 1, 1) # apply the texture self.targetRoot = self.target if 'textureRoot' in target: self.targetRoot = self.target.find("**/" + target['textureRoot']) assert self.targetRoot self.targetRoot.setTexture(self.typingStage, self.tex) #self.setupTargetClip() # reset self.paperX = self.paperY = 0. newPos = self.calcPaperPos(self.paperY) self.target.setPos(newPos) self.moveCarriage() def setupTargetClip(self): """ The target is fed in to the typewriter but until we invent "geom curling", it shouldn't be visible under the typewriter under the desk. The @underDeskClip node has a world-relative bounding box, which we can convert to the target-relative bounding box, and pass to a shader that can clip the nodes. """ shader = Shader.make( Shader.SLGLSL, """ #version 120 attribute vec4 p3d_MultiTexCoord0; attribute vec4 p3d_MultiTexCoord1; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = p3d_MultiTexCoord0; gl_TexCoord[1] = p3d_MultiTexCoord1; } """, """ #version 120 uniform sampler2D baseTex; uniform sampler2D charTex; const vec4 zero = vec4(0, 0, 0, 0); const vec4 one = vec4(1, 1, 1, 1); const vec4 half = vec4(0.5, 0.5, 0.5, 0); void main() { vec4 baseColor = texture2D(baseTex, gl_TexCoord[0].st); vec4 typeColor = texture2D(charTex, gl_TexCoord[1].st); gl_FragColor = baseColor * typeColor; }""" ) self.target.setShader(shader) baseTex = self.targetRoot.getTexture() print "Base Texture:",baseTex self.target.setShaderInput("baseTex", baseTex) self.target.setShaderInput("charTex", self.tex) def hookKeyboard(self): """ Hook events so we can respond to keypresses. """ self.base.buttonThrowers[0].node().setKeystrokeEvent('keystroke') self.base.accept('keystroke', self.schedTypeCharacter) self.base.accept('backspace', self.schedBackspace) self.base.accept('arrow_up', lambda: self.schedAdjustPaper(-5)) self.base.accept('arrow_up-repeat', lambda: self.schedAdjustPaper(-1)) self.base.accept('arrow_down', lambda:self.schedAdjustPaper(5)) self.base.accept('arrow_down-repeat', lambda:self.schedAdjustPaper(1)) self.base.accept('arrow_left', lambda: self.schedAdjustCarriage(-1)) self.base.accept('arrow_left-repeat', lambda: self.schedAdjustCarriage(-1)) self.base.accept('arrow_right', lambda:self.schedAdjustCarriage(1)) self.base.accept('arrow_right-repeat', lambda:self.schedAdjustCarriage(1)) def paperCharWidth(self, pixels=None): if not pixels: pixels = self.fontCharSize[0] return float(pixels) / self.tex.getXSize() def paperLineHeight(self): return float(self.fontCharSize[1] * 1.2) / self.tex.getYSize() def schedScroll(self): if self.scheduler.isQueueEmpty(): self.schedRollPaper(1) self.schedResetCarriage() def schedBackspace(self): if self.scheduler.isQueueEmpty(): def doit(): if self.paperX > 0: self.schedAdjustCarriage(-1) self.scheduler.schedule(0.01, doit) def createMoveCarriageInterval(self, newX, curX=None): if curX is None: curX = self.paperX here = self.calcCarriage(curX) there = self.calcCarriage(newX) posInterval = LerpPosInterval( self.carriageNP, abs(newX - curX), there, startPos = here, blendType='easeIn') posInterval.setDoneEvent('carriageReset') def isReset(): self.paperX = newX self.base.acceptOnce('carriageReset', isReset) return posInterval def schedResetCarriage(self): if self.paperX > 0.1: self.sounds['pullback'].play() invl = self.createMoveCarriageInterval(0) self.scheduler.scheduleInterval(0, invl) def calcCarriage(self, paperX): """ Calculate where the carriage should be offset based on the position on the paper :param paperX: 0...1 :return: pos for self.carriageNP """ x = (0.5 - paperX) * 0.69 * 0.8 + 0.01 bb = self.carriageBounds return self.baseCarriagePos + Point3(x * (bb[1].x-bb[0].x), 0, 0) def moveCarriage(self): pos = self.calcCarriage(self.paperX) self.carriageNP.setPos(pos) def schedMoveCarriage(self, curX, newX): if self.scheduler.isQueueEmpty(): #self.scheduler.schedule(0.1, self.moveCarriage) invl = self.createMoveCarriageInterval(newX, curX=curX) invl.start() def schedAdjustCarriage(self, bx): if self.scheduler.isQueueEmpty(): def doit(): self.paperX = max(0.0, min(1.0, self.paperX + bx * self.paperCharWidth())) self.moveCarriage() self.scheduler.schedule(0.1, doit) def calcPaperPos(self, paperY): # center over roller, peek out a little z = paperY * 0.8 - 0.5 + 0.175 bb = self.target.getTightBounds() return Point3(-0.5, 0, z * (bb[1].z-bb[0].z)) def createMovePaperInterval(self, newY): here = self.calcPaperPos(self.paperY) there = self.calcPaperPos(newY) posInterval = LerpPosInterval( self.target, abs(newY - self.paperY), there, startPos = here, blendType='easeInOut') posInterval.setDoneEvent('scrollDone') def isDone(): self.paperY = newY self.base.acceptOnce('scrollDone', isDone) return posInterval def schedAdjustPaper(self, by): if self.scheduler.isQueueEmpty(): def doit(): self.schedRollPaper(by) self.scheduler.schedule(0.1, doit) def schedRollPaper(self, by): """ Position the paper such that @percent of it is rolled over roller :param percent: :return: """ def doit(): self.sounds['scroll'].play() newY = min(1.0, max(0.0, self.paperY + self.paperLineHeight() * by)) invl = self.createMovePaperInterval(newY) invl.start() self.scheduler.schedule(0.1, doit) def schedTypeCharacter(self, keyname): # filter for visibility if ord(keyname) == 13: self.schedScroll() elif ord(keyname) >= 32 and ord(keyname) != 127: if self.scheduler.isQueueEmpty(): curX, curY = self.paperX, self.paperY self.typeCharacter(keyname, curX, curY) def typeCharacter(self, ch, curX, curY): newX = curX w, h = self.drawCharacter(ch, curX, curY) newX += w if ch != ' ': # alternate typing sound #self.typeIndex = (self.typeIndex+1) % 3 self.typeIndex = random.randint(0, 2) self.sounds['type' + str(self.typeIndex+1)].play() else: self.sounds['advance'].play() if newX >= 1: self.sounds['bell'].play() newX = 1 self.schedMoveCarriage(self.paperX, newX) # move first, to avoid overtype self.paperX = newX
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) #noise = PerlinNoise3(32, 32, 32, 256) #noise = PerlinNoise3()
def __init__(self, mesh_path, progressive_texture_path): resolutions = [] f = tarfile.open(progressive_texture_path) for resolution_name in f.getnames(): toset = { 'size': resolution_name[:-4], 'contents': f.extractfile(resolution_name).read() } texpnm = PNMImage() texpnm.read(StringStream(toset['contents']), 'something.jpg') newtex = Texture() newtex.load(texpnm) toset['texture'] = newtex resolutions.append(toset) self.resolutions = resolutions def aux_loader(fname): return resolutions[0]['contents'] mesh = collada.Collada(mesh_path, aux_file_loader=aux_loader) scene_members = getSceneMembers(mesh) base = ShowBase() rotateNode = GeomNode("rotater") rotatePath = render.attachNewNode(rotateNode) matrix = numpy.identity(4) if mesh.assetInfo.upaxis == collada.asset.UP_AXIS.X_UP: r = collada.scene.RotateTransform(0, 1, 0, 90) matrix = r.matrix elif mesh.assetInfo.upaxis == collada.asset.UP_AXIS.Y_UP: r = collada.scene.RotateTransform(1, 0, 0, 90) matrix = r.matrix rotatePath.setMat(Mat4(*matrix.T.flatten().tolist())) geom, renderstate, mat4 = scene_members[0] node = GeomNode("primitive") node.addGeom(geom) if renderstate is not None: node.setGeomState(0, renderstate) self.geomPath = rotatePath.attachNewNode(node) self.geomPath.setMat(mat4) wrappedNode = ensureCameraAt(self.geomPath, base.camera) base.disableMouse() attachLights(render) render.setShaderAuto() render.setTransparency(TransparencyAttrib.MDual, 1) base.render.analyze() KeyboardMovement() MouseDrag(wrappedNode) MouseScaleZoom(wrappedNode) MouseCamera() num_resolutions = len(resolutions) - 1 self.slider = DirectSlider(range=(0, num_resolutions), value=0, pageSize=1, command=self.sliderMoved, pos=(0, 0, -.9), scale=1) for key, val in uiArgs.iteritems(): self.slider.thumb[key] = val self.triText = OnscreenText(text="", pos=(-1, 0.85), scale=0.15, fg=(1, 0.5, 0.5, 1), align=TextNode.ALeft, mayChange=1) base.run()
class GPUFFT(DebugObject): """ This is a collection of compute shaders to generate the inverse fft efficiently on the gpu, with butterfly FFT and precomputed weights """ def __init__(self, N, sourceTex, normalizationFactor): """ Creates a new fft instance. The source texture has to specified from the begining, as the shaderAttributes are pregenerated for performance reasons """ DebugObject.__init__(self, "GPU-FFT") self.size = N self.log2Size = int(math.log(N, 2)) self.normalizationFactor = normalizationFactor # Create a ping and a pong texture, because we can't write to the # same texture while reading to it (that would lead to unexpected # behaviour, we could solve that by using an appropriate thread size, # but it works fine so far) self.pingTexture = Texture("FFTPing") self.pingTexture.setup2dTexture( self.size, self.size, Texture.TFloat, Texture.FRgba32) self.pongTexture = Texture("FFTPong") self.pongTexture.setup2dTexture( self.size, self.size, Texture.TFloat, Texture.FRgba32) self.sourceTex = sourceTex for tex in [self.pingTexture, self.pongTexture, sourceTex]: tex.setMinfilter(Texture.FTNearest) tex.setMagfilter(Texture.FTNearest) tex.setWrapU(Texture.WMClamp) tex.setWrapV(Texture.WMClamp) # Pregenerate weights & indices for the shaders self._computeWeighting() # Pre generate the shaders, we have 2 passes: Horizontal and Vertical # which both execute log2(N) times with varying radii self.horizontalFFTShader = BetterShader.loadCompute( "Shader/Water/HorizontalFFT.compute") self.horizontalFFT = NodePath("HorizontalFFT") self.horizontalFFT.setShader(self.horizontalFFTShader) self.horizontalFFT.setShaderInput( "precomputedWeights", self.weightsLookupTex) self.horizontalFFT.setShaderInput("N", LVecBase2i(self.size)) self.verticalFFTShader = BetterShader.loadCompute( "Shader/Water/VerticalFFT.compute") self.verticalFFT = NodePath("VerticalFFT") self.verticalFFT.setShader(self.verticalFFTShader) self.verticalFFT.setShaderInput( "precomputedWeights", self.weightsLookupTex) self.verticalFFT.setShaderInput("N", LVecBase2i(self.size)) # Create a texture where the result is stored self.resultTexture = Texture("Result") self.resultTexture.setup2dTexture( self.size, self.size, Texture.TFloat, Texture.FRgba16) self.resultTexture.setMinfilter(Texture.FTLinear) self.resultTexture.setMagfilter(Texture.FTLinear) # Prepare the shader attributes, so we don't have to regenerate them # every frame -> That is VERY slow (3ms per fft instance) self._prepareAttributes() def getResultTexture(self): """ Returns the result texture, only contains valid data after execute was called at least once """ return self.resultTexture def _generateIndices(self, storageA, storageB): """ This method generates the precompute indices, see http://cnx.org/content/m12012/latest/image1.png """ numIter = self.size offset = 1 step = 0 for i in xrange(self.log2Size): numIter = numIter >> 1 step = offset for j in xrange(self.size): goLeft = (j / step) % 2 == 1 indexA, indexB = 0, 0 if goLeft: indexA, indexB = j - step, j else: indexA, indexB = j, j + step storageA[i][j] = indexA storageB[i][j] = indexB offset = offset << 1 def _generateWeights(self, storage): """ This method generates the precomputed weights """ # Using a custom pi variable should force the calculations to use # high precision (I hope so) pi = 3.141592653589793238462643383 numIter = self.size / 2 numK = 1 resolutionFloat = float(self.size) for i in xrange(self.log2Size): start = 0 end = 2 * numK for b in xrange(numIter): K = 0 for k in xrange(start, end, 2): fK = float(K) fNumIter = float(numIter) weightA = Vec2( math.cos(2.0 * pi * fK * fNumIter / resolutionFloat), -math.sin(2.0 * pi * fK * fNumIter / resolutionFloat)) weightB = Vec2( -math.cos(2.0 * pi * fK * fNumIter / resolutionFloat), math.sin(2.0 * pi * fK * fNumIter / resolutionFloat)) storage[i][k / 2] = weightA storage[i][k / 2 + numK] = weightB K += 1 start += 4 * numK end = start + 2 * numK numIter = numIter >> 1 numK = numK << 1 def _reverseRow(self, indices): """ Reverses the bits in the given row. This is required for inverse fft (actually we perform a normal fft, but reversing the bits gives us an inverse fft) """ mask = 0x1 for j in xrange(self.size): val = 0x0 temp = int(indices[j]) # Int is required, for making a copy for i in xrange(self.log2Size): t = mask & temp val = (val << 1) | t temp = temp >> 1 indices[j] = val def _computeWeighting(self): """ Precomputes the weights & indices, and stores them in a texture """ indicesA = [[0 for i in xrange(self.size)] for k in xrange(self.log2Size)] indicesB = [[0 for i in xrange(self.size)] for k in xrange(self.log2Size)] weights = [[Vec2(0.0) for i in xrange(self.size)] for k in xrange(self.log2Size)] self.debug("Pre-Generating indices ..") self._generateIndices(indicesA, indicesB) self._reverseRow(indicesA[0]) self._reverseRow(indicesB[0]) self.debug("Pre-Generating weights ..") self._generateWeights(weights) # Create storage for the weights & indices self.weightsLookup = PNMImage(self.size, self.log2Size, 4) self.weightsLookup.setMaxval((2 ** 16) - 1) self.weightsLookup.fill(0.0) # Populate storage for x in xrange(self.size): for y in xrange(self.log2Size): indexA = indicesA[y][x] indexB = indicesB[y][x] weight = weights[y][x] self.weightsLookup.setRed(x, y, indexA / float(self.size)) self.weightsLookup.setGreen(x, y, indexB / float(self.size)) self.weightsLookup.setBlue(x, y, weight.x * 0.5 + 0.5) self.weightsLookup.setAlpha(x, y, weight.y * 0.5 + 0.5) # Convert storage to texture so we can use it in a shader self.weightsLookupTex = Texture("Weights Lookup") self.weightsLookupTex.load(self.weightsLookup) self.weightsLookupTex.setFormat(Texture.FRgba16) self.weightsLookupTex.setMinfilter(Texture.FTNearest) self.weightsLookupTex.setMagfilter(Texture.FTNearest) self.weightsLookupTex.setWrapU(Texture.WMClamp) self.weightsLookupTex.setWrapV(Texture.WMClamp) def _prepareAttributes(self): """ Prepares all shaderAttributes, so that we have a list of ShaderAttributes we can simply walk through in the update method, that is MUCH faster than using setShaderInput, as each call to setShaderInput forces the generation of a new ShaderAttrib """ self.attributes = [] textures = [self.pingTexture, self.pongTexture] currentIndex = 0 firstPass = True # Horizontal for step in xrange(self.log2Size): source = textures[currentIndex] dest = textures[1 - currentIndex] if firstPass: source = self.sourceTex firstPass = False index = self.log2Size - step - 1 self.horizontalFFT.setShaderInput("source", source) self.horizontalFFT.setShaderInput("dest", dest) self.horizontalFFT.setShaderInput( "butterflyIndex", LVecBase2i(index)) self._queueShader(self.horizontalFFT) currentIndex = 1 - currentIndex # Vertical for step in xrange(self.log2Size): source = textures[currentIndex] dest = textures[1 - currentIndex] isLastPass = step == self.log2Size - 1 if isLastPass: dest = self.resultTexture index = self.log2Size - step - 1 self.verticalFFT.setShaderInput("source", source) self.verticalFFT.setShaderInput("dest", dest) self.verticalFFT.setShaderInput( "isLastPass", isLastPass) self.verticalFFT.setShaderInput( "normalizationFactor", self.normalizationFactor) self.verticalFFT.setShaderInput( "butterflyIndex", LVecBase2i(index)) self._queueShader(self.verticalFFT) currentIndex = 1 - currentIndex def execute(self): """ Executes the inverse fft once """ for attr in self.attributes: self._executeShader(attr) def _queueShader(self, node): """ Internal method to fetch the ShaderAttrib of a node and store it in the update queue """ sattr = node.getAttrib(ShaderAttrib) self.attributes.append(sattr) def _executeShader(self, sattr): """ Internal method to execute a shader by a given ShaderAttrib """ Globals.base.graphicsEngine.dispatch_compute( (self.size / 16, self.size / 16, 1), sattr, Globals.base.win.get_gsg())
class IESLoader(DebugObject): """ This class manages the loading of IES Profiles and combining them into a texture so they can be used in a shader """ # All supported IES Profile versions IESVersionTable = { 'IESNA:LM-63-1986': 1986, 'IESNA:LM-63-1991': 1991, 'IESNA91': 1991, 'IESNA:LM-63-1995': 1995, 'IESNA:LM-63-2002': 2002, # Seems they have their own format "ERCO Leuchten GmbH BY: ERCO/LUM650/8701": 2003, "ERCO Leuchten GmbH": 2003, } # Controls the size of the precomputed ies tables. Bigger values mean more # precision but also more storage required IESTableResolution = 256 def __init__(self): """ Creates a new IES Loader """ DebugObject.__init__(self, "IESLoader") self.storage = Texture("IESProfiles") self.storage.setup2dTextureArray(self.IESTableResolution, 1, 64, Texture.TFloat, Texture.FRgba16) self.storage.setMinfilter(SamplerState.FTLinear) self.storage.setMagfilter(SamplerState.FTLinear) self.storage.setWrapU(SamplerState.WMClamp) self.storage.setWrapV(SamplerState.WMClamp) self.storage.setWrapW(SamplerState.WMClamp) self.profileNames = [] def getIESProfileStorageTex(self): """ Returns the texture array where all ies profiles are stored in """ return self.storage def getIESProfileIndexByName(self, name): """ Returns the ies profile index if loaded so far, or -1 if no ies profile with that name exists so far """ if name in self.profileNames: return self.profileNames.index(name) return -1 def loadIESProfiles(self, directory): """ Loads all ies profiles from a given directory """ self.debug("Loading IES Profiles from", directory) files = listdir(directory) for entry in files: if entry.lower().endswith(".ies"): combinedPath = os.path.join(directory, entry) self._loadIESProfile(entry.split(".")[0], combinedPath) def _storeIESProfile(self, name, lampRadialGradientData, lampGradientData): """ Internal method to convert the array of data into a texture to load into the texture array """ def interpolateValue(dataset, percentageVal): """ Interpolates over an array, accepting float values from 0.0 to 1.0 """ percentageValClamped = max(0.0, min(0.99999, percentageVal)) scaledVal = percentageValClamped * len(dataset) index = int(scaledVal) indexBy1 = min(index + 1, len(dataset) -1) lerpFactor = scaledVal % 1.0 return dataset[indexBy1] * lerpFactor + dataset[index] * (1.0 - lerpFactor) # Add profile name to the list of loaded profiles if name in self.profileNames: # self.error("Cannot register profile",name,"twice") return False profileIndex = len(self.profileNames) self.profileNames.append(name) # Generate gradient texture img = PNMImage(self.IESTableResolution, 1, 4, 2 ** 16 - 1) for offset in xrange(self.IESTableResolution): radialGradientValR = interpolateValue(lampRadialGradientData, (offset + 5.0) / float(self.IESTableResolution)) radialGradientValG = interpolateValue(lampRadialGradientData, offset / float(self.IESTableResolution)) radialGradientValB = interpolateValue(lampRadialGradientData, (offset - 5.0) / float(self.IESTableResolution)) gradientVal = interpolateValue(lampGradientData, offset / float(self.IESTableResolution)) img.setXelA(offset, 0, radialGradientValR, radialGradientValG, radialGradientValB, gradientVal) # Store gradient texture self.storage.load(img, profileIndex, 0) def _loadIESProfile(self, name, filename): """ Internal method to load an ies profile. Adapted from https://gist.githubusercontent.com/AngryLoki/4364512/raw/ies2cycles.py """ # self.debug("Loading ies profile", filename, "as",len(self.profileNames)) profileMultiplier = 1.0 # Open the IES file with open(filename, 'r') as handle: content = handle.read() # Extract and check version string versionString, content = content.split('\n', 1) versionString = versionString.strip() if versionString in self.IESVersionTable: version = self.IESVersionTable[versionString] else: self.warn("No supported IES version found:",versionString) version = None # Extract IES properties keywords = dict() while content and not content.startswith('TILT='): key, content = content.split('\n', 1) if key.startswith('['): endbracket = key.find(']') if endbracket != -1: keywords[key[1:endbracket]] = key[endbracket + 1:].strip() # After all properties, the tile keyword should follow keyword, content = content.split('\n', 1) if not keyword.startswith('TILT'): self.warn("TILT keyword not found") return False # Strip data fileData = content.replace(',', ' ').split() # Property 0 is the amount of lamps numLamps = int(fileData[0]) if numLamps != 1: self.warn("Only 1 lamp is supported,", numLamps, "found:",name) # Extract further properties lumensPerLamp = float(fileData[1]) candelaMultiplier = float(fileData[2]) numVerticalAngles = int(fileData[3]) numHorizontalAngles = int(fileData[4]) # Check if everything went right so far if not numVerticalAngles or not numHorizontalAngles: self.error("Error during property extract") return False # Extract further properties photometricType = int(fileData[5]) unitType = int(fileData[6]) # Determine the unit type, either feet or meters if unitType not in [1, 2]: self.warn("Unkown unity type:", unitType) # Extract data size width, length, height = map(float, fileData[7:10]) ballastFactor = float(fileData[10]) futureUse = float(fileData[11]) if futureUse != 1.0: self.warn("Invalid future use field") inputWatts = float(fileData[12]) # Extract the actual data verticalAngles = [float(s) for s in fileData[13:13 + numVerticalAngles]] horizontalAngles = [float(s) for s in fileData[13 + numVerticalAngles: 13 + numVerticalAngles + numHorizontalAngles]] # Determine the vertical light cone type. There are 90 and 180 degree cone types. if verticalAngles[0] == 0 and verticalAngles[-1] == 90: lampConeType = 'TYPE90' elif verticalAngles[0] == 0 and verticalAngles[-1] == 180: lampConeType = 'TYPE180' else: self.warn("Unsupported angles: ", verticalAngles[0], "-", verticalAngles[-1]) lampConeType = 'TYPE180' # Determine the horizontal light cone type if len(horizontalAngles) == 1 or abs(horizontalAngles[0] - horizontalAngles[-1]) == 360: lampHorizontalConeType = 'TYPE360' elif abs(horizontalAngles[0] - horizontalAngles[-1]) == 180: lampHorizontalConeType = 'TYPE180' elif abs(horizontalAngles[0] - horizontalAngles[-1]) == 90: lampHorizontalConeType = 'TYPE90' else: self.warn("Unsupported horizontal angles: ", horizontalAngles[0], "-", horizontalAngles[-1]) lampHorizontalConeType = 'TYPE360' # Read the candela values offset = 13 + len(verticalAngles) + len(horizontalAngles) candelaIndex = len(verticalAngles) * len(horizontalAngles) candelaValues = [float(s) for s in fileData[offset:offset + candelaIndex]] # Convert the 1d candela array to 2d array candela2D = list(zip(*[iter(candelaValues)] * len(verticalAngles))) # Compute the fallof gradient lampGradientData = [x / verticalAngles[-1] for x in verticalAngles] # Compute the radial gradient radialGradientData = [sum(x) / len(x) for x in zip(*candela2D)] radialGradientDataMax = max(radialGradientData) # Normalize the radial gradient by dividing by the maximum value radialGradientData = [val / radialGradientDataMax for val in radialGradientData] # Finally register the profile self._storeIESProfile(name, radialGradientData, lampGradientData) return True
class IESLoader(DebugObject): """ This class manages the loading of IES Profiles and combining them into a texture so they can be used in a shader """ # All supported IES Profile versions IESVersionTable = { 'IESNA:LM-63-1986': 1986, 'IESNA:LM-63-1991': 1991, 'IESNA91': 1991, 'IESNA:LM-63-1995': 1995, 'IESNA:LM-63-2002': 2002, # Seems they have their own format "ERCO Leuchten GmbH BY: ERCO/LUM650/8701": 2003, "ERCO Leuchten GmbH": 2003, } # Controls the size of the precomputed ies tables. Bigger values mean more # precision but also more storage required IESTableResolution = 256 def __init__(self): """ Creates a new IES Loader """ DebugObject.__init__(self, "IESLoader") self.storage = Texture("IESProfiles") self.storage.setup2dTextureArray(self.IESTableResolution, 1, 64, Texture.TFloat, Texture.FRgba16) self.storage.setMinfilter(SamplerState.FTLinear) self.storage.setMagfilter(SamplerState.FTLinear) self.storage.setWrapU(SamplerState.WMClamp) self.storage.setWrapV(SamplerState.WMClamp) self.storage.setWrapW(SamplerState.WMClamp) self.profileNames = [] def getIESProfileStorageTex(self): """ Returns the texture array where all ies profiles are stored in """ return self.storage def getIESProfileIndexByName(self, name): """ Returns the ies profile index if loaded so far, or -1 if no ies profile with that name exists so far """ if name in self.profileNames: return self.profileNames.index(name) return -1 def loadIESProfiles(self, directory): """ Loads all ies profiles from a given directory """ self.debug("Loading IES Profiles from", directory) files = listdir(directory) for entry in files: if entry.lower().endswith(".ies"): combinedPath = os.path.join(directory, entry) self._loadIESProfile(entry.split(".")[0], combinedPath) def _storeIESProfile(self, name, lampRadialGradientData, lampGradientData): """ Internal method to convert the array of data into a texture to load into the texture array """ def interpolateValue(dataset, percentageVal): """ Interpolates over an array, accepting float values from 0.0 to 1.0 """ percentageValClamped = max(0.0, min(0.99999, percentageVal)) scaledVal = percentageValClamped * len(dataset) index = int(scaledVal) indexBy1 = min(index + 1, len(dataset) - 1) lerpFactor = scaledVal % 1.0 return dataset[indexBy1] * lerpFactor + dataset[index] * ( 1.0 - lerpFactor) # Add profile name to the list of loaded profiles if name in self.profileNames: # self.error("Cannot register profile",name,"twice") return False profileIndex = len(self.profileNames) self.profileNames.append(name) # Generate gradient texture img = PNMImage(self.IESTableResolution, 1, 4, 2**16 - 1) for offset in xrange(self.IESTableResolution): radialGradientValR = interpolateValue( lampRadialGradientData, (offset + 5.0) / float(self.IESTableResolution)) radialGradientValG = interpolateValue( lampRadialGradientData, offset / float(self.IESTableResolution)) radialGradientValB = interpolateValue( lampRadialGradientData, (offset - 5.0) / float(self.IESTableResolution)) gradientVal = interpolateValue( lampGradientData, offset / float(self.IESTableResolution)) img.setXelA(offset, 0, radialGradientValR, radialGradientValG, radialGradientValB, gradientVal) # Store gradient texture self.storage.load(img, profileIndex, 0) def _loadIESProfile(self, name, filename): """ Internal method to load an ies profile. Adapted from https://gist.githubusercontent.com/AngryLoki/4364512/raw/ies2cycles.py """ # self.debug("Loading ies profile", filename, "as",len(self.profileNames)) profileMultiplier = 1.0 # Open the IES file with open(filename, 'r') as handle: content = handle.read() # Extract and check version string versionString, content = content.split('\n', 1) versionString = versionString.strip() if versionString in self.IESVersionTable: version = self.IESVersionTable[versionString] else: self.warn("No supported IES version found:", versionString) version = None # Extract IES properties keywords = dict() while content and not content.startswith('TILT='): key, content = content.split('\n', 1) if key.startswith('['): endbracket = key.find(']') if endbracket != -1: keywords[key[1:endbracket]] = key[endbracket + 1:].strip() # After all properties, the tile keyword should follow keyword, content = content.split('\n', 1) if not keyword.startswith('TILT'): self.warn("TILT keyword not found") return False # Strip data fileData = content.replace(',', ' ').split() # Property 0 is the amount of lamps numLamps = int(fileData[0]) if numLamps != 1: self.warn("Only 1 lamp is supported,", numLamps, "found:", name) # Extract further properties lumensPerLamp = float(fileData[1]) candelaMultiplier = float(fileData[2]) numVerticalAngles = int(fileData[3]) numHorizontalAngles = int(fileData[4]) # Check if everything went right so far if not numVerticalAngles or not numHorizontalAngles: self.error("Error during property extract") return False # Extract further properties photometricType = int(fileData[5]) unitType = int(fileData[6]) # Determine the unit type, either feet or meters if unitType not in [1, 2]: self.warn("Unkown unity type:", unitType) # Extract data size width, length, height = map(float, fileData[7:10]) ballastFactor = float(fileData[10]) futureUse = float(fileData[11]) if futureUse != 1.0: self.warn("Invalid future use field") inputWatts = float(fileData[12]) # Extract the actual data verticalAngles = [ float(s) for s in fileData[13:13 + numVerticalAngles] ] horizontalAngles = [ float(s) for s in fileData[13 + numVerticalAngles:13 + numVerticalAngles + numHorizontalAngles] ] # Determine the vertical light cone type. There are 90 and 180 degree cone types. if verticalAngles[0] == 0 and verticalAngles[-1] == 90: lampConeType = 'TYPE90' elif verticalAngles[0] == 0 and verticalAngles[-1] == 180: lampConeType = 'TYPE180' else: self.warn("Unsupported angles: ", verticalAngles[0], "-", verticalAngles[-1]) lampConeType = 'TYPE180' # Determine the horizontal light cone type if len(horizontalAngles) == 1 or abs(horizontalAngles[0] - horizontalAngles[-1]) == 360: lampHorizontalConeType = 'TYPE360' elif abs(horizontalAngles[0] - horizontalAngles[-1]) == 180: lampHorizontalConeType = 'TYPE180' elif abs(horizontalAngles[0] - horizontalAngles[-1]) == 90: lampHorizontalConeType = 'TYPE90' else: self.warn("Unsupported horizontal angles: ", horizontalAngles[0], "-", horizontalAngles[-1]) lampHorizontalConeType = 'TYPE360' # Read the candela values offset = 13 + len(verticalAngles) + len(horizontalAngles) candelaIndex = len(verticalAngles) * len(horizontalAngles) candelaValues = [ float(s) for s in fileData[offset:offset + candelaIndex] ] # Convert the 1d candela array to 2d array candela2D = list(zip(*[iter(candelaValues)] * len(verticalAngles))) # Compute the fallof gradient lampGradientData = [x / verticalAngles[-1] for x in verticalAngles] # Compute the radial gradient radialGradientData = [sum(x) / len(x) for x in zip(*candela2D)] radialGradientDataMax = max(radialGradientData) # Normalize the radial gradient by dividing by the maximum value radialGradientData = [ val / radialGradientDataMax for val in radialGradientData ] # Finally register the profile self._storeIESProfile(name, radialGradientData, lampGradientData) return True
class MazeMapGui(DirectFrame): notify = directNotify.newCategory('MazeMapGui') def __init__(self, mazeCollTable, maskResolution = None, radiusRatio = None, bgColor = (0.8, 0.8, 0.8), fgColor = (0.5, 0.5, 0.5, 1.0)): DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) self.hide() self._bgColor = bgColor self._fgColor = fgColor self._mazeCollTable = mazeCollTable self._mazeWidth = len(self._mazeCollTable[0]) self._mazeHeight = len(self._mazeCollTable) self._maskResolution = maskResolution or DEFAULT_MASK_RESOLUTION if radiusRatio is None: self._radius = self._maskResolution * DEFAULT_RADIUS_RATIO else: self._radius = self._maskResolution * radiusRatio self._revealedCells = [] for y in xrange(self._mazeHeight): self._revealedCells.append([]) for u in xrange(self._mazeWidth): self._revealedCells[y].append(False) self._revealFunctions = {MazeRevealType.SmoothCircle: self._revealSmoothCircle, MazeRevealType.HardCircle: self._revealHardCircle, MazeRevealType.Square: self._revealSquare} self._revealFunction = MAZE_REVEAL_TYPE self.map = self._createMapTextureCard() self.map.reparentTo(self) self.maskedLayer = self.attachNewNode('maskedLayer') self.mask = self._createMaskTextureCard() self.mask.reparentTo(self) self.visibleLayer = self.attachNewNode('visibleLayer') self._laffMeterModel = loader.loadModel('phase_3/models/gui/laff_o_meter') self._toon2marker = {} return def _createMapTextureCard(self): mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION) mapImage.fill(*self._bgColor) fgColor = VBase4D(*self._fgColor) for x in xrange(self._mazeHeight): for y in xrange(self._mazeWidth): if self._mazeCollTable[y][x] == 1: ax = float(x) / self._mazeWidth * MAP_RESOLUTION invertedY = self._mazeHeight - 1 - y ay = float(invertedY) / self._mazeHeight * MAP_RESOLUTION self._drawSquare(mapImage, int(ax), int(ay), 10, fgColor) mapTexture = Texture('mapTexture') mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) mapTexture.setMinfilter(Texture.FTLinear) mapTexture.load(mapImage) mapTexture.setWrapU(Texture.WMClamp) mapTexture.setWrapV(Texture.WMClamp) mapImage.clear() del mapImage cm = CardMaker('map_cardMaker') cm.setFrame(-1.0, 1.0, -1.0, 1.0) map = self.attachNewNode(cm.generate()) map.setTexture(mapTexture, 1) return map def _createMaskTextureCard(self): self._maskImage = PNMImage(self._maskResolution, self._maskResolution, 4) for x in xrange(self._maskResolution): for y in xrange(self._maskResolution): self._maskImage.setXelA(x, y, 0, 0, 0, 1) self.maskTexture = Texture('maskTexture') self.maskTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) self.maskTexture.setMinfilter(Texture.FTLinear) self.maskTexture.setWrapU(Texture.WMClamp) self.maskTexture.setWrapV(Texture.WMClamp) self.maskTexture.load(self._maskImage) base.graphicsEngine.renderFrame() cm = CardMaker('mask_cardMaker') cm.setFrame(-1.1, 1.1, -1.1, 1.1) mask = self.attachNewNode(cm.generate()) mask.setTexture(self.maskTexture, 1) mask.setTransparency(1) return mask def _drawSquare(self, image, ulx, uly, size, color): x = int(ulx) while x <= ulx + size: y = int(uly) while y <= uly + size: if x > 0 and y > 0 and x < image.getXSize() and y < image.getYSize(): image.setXelA(x, y, color) y += 1 x += 1 def destroy(self): del self._mazeCollTable del self._maskResolution del self._radius del self._revealedCells del self._revealFunctions del self._revealFunction self.map.removeNode() del self.map self.mask.removeNode() del self.mask self.maskedLayer.removeNode() del self.maskedLayer self.visibleLayer.removeNode() del self.visibleLayer self._maskImage.clear() del self._maskImage self.maskTexture.clear() del self.maskTexture self._laffMeterModel.removeNode() del self._laffMeterModel DirectFrame.destroy(self) def _revealSmoothCircle(self, x, y, center): length = (Vec2(x, y) - center).length() goalAlpha = max(0.0, length / float(self._radius) - 0.5) self._maskImage.setXelA(x, y, VBase4D(0.0, 0.0, 0.0, min(self._maskImage.getAlpha(x, y), goalAlpha * 2.0))) def _revealHardCircle(self, x, y, center): length = (Vec2(x, y) - center).length() if length <= self._radius: self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0)) def _revealSquare(self, x, y, center): self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0)) def _drawHole(self, x, y): center = Vec2(x, y) ul = center - Vec2(self._radius, self._radius) lr = center + Vec2(self._radius, self._radius) x = int(ul[0]) while x <= lr[0]: y = int(ul[1]) while y <= lr[1]: if x > 0 and y > 0 and x < self._maskResolution and y < self._maskResolution: self._revealFunctions[self._revealFunction](x, y, center) y += 1 x += 1 self.maskTexture.load(self._maskImage) self.mask.setTexture(self.maskTexture, 1) def _createSimpleMarker(self, size, color = (1, 1, 1)): halfSize = size * 0.5 cm = CardMaker('mazemap_simple_marker') cm.setFrame(-halfSize, halfSize, -halfSize, halfSize) markerNP = self.maskedLayer.attachNewNode(cm.generate()) markerNP.setColor(*color) return markerNP def tile2gui(self, x, y): y = self._mazeHeight - y cellWidth = self._maskResolution / self._mazeWidth cellHeight = self._maskResolution / self._mazeHeight ax = float(x) / self._mazeWidth * self._maskResolution ax += cellWidth ay = float(y) / self._mazeHeight * self._maskResolution ay += cellHeight return (ax, ay) def gui2pos(self, x, y): return (x / self._maskResolution * 2.0 - 0.97, 0, y / self._maskResolution * -2.0 + 1.02) def _getToonMarker(self, toon): hType = toon.style.getType() if hType == 'rabbit': hType = 'bunny' return self._laffMeterModel.find('**/' + hType + 'head') def addToon(self, toon, tX, tY): marker = NodePath('toon_marker-%i' % toon.doId) marker.reparentTo(self) self._getToonMarker(toon).copyTo(marker) marker.setColor(toon.style.getHeadColor()) if toon.isLocal(): marker.setScale(0.07) else: marker.setScale(0.05) marker.flattenStrong() marker.setPos(*self.gui2pos(*self.tile2gui(tX, tY))) self._toon2marker[toon] = marker def removeToon(self, toon): if toon not in self._toon2marker: return self._toon2marker[toon].removeNode() del self._toon2marker[toon] def updateToon(self, toon, tX, tY): if toon not in self._toon2marker: return x, y = self.tile2gui(tX, tY) self._toon2marker[toon].setPos(*self.gui2pos(x, y)) if tY < 0 or tY >= len(self._revealedCells): self.notify.warning('updateToon earlying out:') self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY)) self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells),)) if len(self._revealedCells) > 0: self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]),)) return if tX < 0 or tX >= len(self._revealedCells[tY]): self.notify.warning('updateToon earlying out:') self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY)) self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells),)) if tY < len(self._revealedCells): self.notify.warning('len(_revealedCells[tY]): %s' % (len(self._revealedCells[tY]),)) elif len(self._revealedCells) > 0: self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]),)) return if not self._revealedCells[tY][tX]: self._drawHole(x, y) self._revealedCells[tY][tX] = True def revealCell(self, x, y): ax, ay = self.tile2gui(x, y) if not self._revealedCells[y][x]: self._drawHole(ax, ay) self._revealedCells[y][x] = True def revealAll(self): for x in xrange(self._maskResolution): for y in xrange(self._maskResolution): self._maskImage.setXelA(x, y, 0, 0, 0, 0) self.revealCell(0, 0) def reset(self): for x in xrange(self._maskResolution): for y in xrange(self._maskResolution): self._maskImage.setXelA(x, y, 0, 0, 0, 1)
class PlayerBase(DirectObject): def __init__(self): # Player Model setup self.player = Actor("Player", {"Run":"Player-Run", "Sidestep":"Player-Sidestep", "Idle":"Player-Idle"}) self.player.setBlend(frameBlend = True) self.player.setPos(0, 0, 0) self.player.pose("Idle", 0) self.player.reparentTo(render) self.player.hide() self.footstep = base.audio3d.loadSfx('footstep.ogg') self.footstep.setLoop(True) base.audio3d.attachSoundToObject(self.footstep, self.player) # Create a brush to paint on the texture splat = PNMImage("../data/Splat.png") self.colorBrush = PNMBrush.makeImage(splat, 6, 6, 1) CamMask = BitMask32.bit(0) AvBufMask = BitMask32.bit(1) self.avbuf = None if base.win: self.avbufTex = Texture('avbuf') self.avbuf = base.win.makeTextureBuffer('avbuf', 256, 256, self.avbufTex, True) cam = Camera('avbuf') cam.setLens(base.camNode.getLens()) self.avbufCam = base.cam.attachNewNode(cam) dr = self.avbuf.makeDisplayRegion() dr.setCamera(self.avbufCam) self.avbuf.setActive(False) self.avbuf.setClearColor((1, 0, 0, 1)) cam.setCameraMask(AvBufMask) base.camNode.setCameraMask(CamMask) # avbuf renders everything it sees with the gradient texture. tex = loader.loadTexture('gradient.png') np = NodePath('np') np.setTexture(tex, 100) np.setColor((1, 1, 1, 1), 100) np.setColorScaleOff(100) np.setTransparency(TransparencyAttrib.MNone, 100) np.setLightOff(100) cam.setInitialState(np.getState()) #render.hide(AvBufMask) # Setup a texture stage to paint on the player self.paintTs = TextureStage('paintTs') self.paintTs.setMode(TextureStage.MDecal) self.paintTs.setSort(10) self.paintTs.setPriority(10) self.tex = Texture('paint_av_%s'%id(self)) # Setup a PNMImage that will hold the paintable texture of the player self.imageSizeX = 64 self.imageSizeY = 64 self.p = PNMImage(self.imageSizeX, self.imageSizeY, 4) self.p.fill(1) self.p.alphaFill(0) self.tex.load(self.p) self.tex.setWrapU(self.tex.WMClamp) self.tex.setWrapV(self.tex.WMClamp) # Apply the paintable texture to the avatar self.player.setTexture(self.paintTs, self.tex) # team self.playerTeam = "" # A lable that will display the players team self.lblTeam = DirectLabel( scale = 1, pos = (0, 0, 3), frameColor = (0, 0, 0, 0), text = "TEAM", text_align = TextNode.ACenter, text_fg = (0,0,0,1)) self.lblTeam.reparentTo(self.player) self.lblTeam.setBillboardPointEye() # basic player values self.maxHits = 3 self.currentHits = 0 self.isOut = False self.TorsorControl = self.player.controlJoint(None,"modelRoot","Torsor") # setup the collision detection # wall and object collision self.playerSphere = CollisionSphere(0, 0, 1, 1) self.playerCollision = self.player.attachNewNode(CollisionNode("playerCollision%d"%id(self))) self.playerCollision.node().addSolid(self.playerSphere) base.pusher.addCollider(self.playerCollision, self.player) base.cTrav.addCollider(self.playerCollision, base.pusher) # foot (walk) collision self.playerFootRay = self.player.attachNewNode(CollisionNode("playerFootCollision%d"%id(self))) self.playerFootRay.node().addSolid(CollisionRay(0, 0, 2, 0, 0, -1)) self.playerFootRay.node().setIntoCollideMask(0) self.lifter = CollisionHandlerFloor() self.lifter.addCollider(self.playerFootRay, self.player) base.cTrav.addCollider(self.playerFootRay, self.lifter) # Player weapon setup self.gunAttach = self.player.exposeJoint(None, "modelRoot", "WeaponSlot_R") self.color = LPoint3f(1, 1, 1) self.gun = Gun(id(self)) self.gun.reparentTo(self.gunAttach) self.gun.hide() self.gun.setColor(self.color) self.hud = None # Player controls setup self.keyMap = {"left":0, "right":0, "forward":0, "backward":0} # screen sizes self.winXhalf = base.win.getXSize() / 2 self.winYhalf = base.win.getYSize() / 2 self.mouseSpeedX = 0.1 self.mouseSpeedY = 0.1 # AI controllable variables self.AIP = 0.0 self.AIH = 0.0 self.movespeed = 5.0 self.userControlled = False self.accept("Bulet-hit-playerCollision%d" % id(self), self.hit) self.accept("window-event", self.recalcAspectRatio) def runBase(self): self.player.show() self.gun.show() taskMgr.add(self.move, "moveTask%d"%id(self), priority=-4) def stopBase(self): taskMgr.remove("moveTask%d"%id(self)) self.ignoreAll() self.gun.remove() self.footstep.stop() base.audio3d.detachSound(self.footstep) self.player.delete() def setKey(self, key, value): self.keyMap[key] = value def setPos(self, pos): self.player.setPos(pos) def setColor(self, color=LPoint3f(0,0,0)): self.color = color self.gun.setColor(color) c = (color[0], color[1], color[2], 1.0) self.lblTeam["text_fg"] = c def setTeam(self, team): self.playerTeam = team self.lblTeam["text"] = team def shoot(self, shotVec=None): self.gun.shoot(shotVec) if self.hud != None: self.hud.updateAmmo(self.gun.maxAmmunition, self.gun.ammunition) def reload(self): self.gun.reload() if self.hud != None: self.hud.updateAmmo(self.gun.maxAmmunition, self.gun.ammunition) def recalcAspectRatio(self, window): self.winXhalf = window.getXSize() / 2 self.winYhalf = window.getYSize() / 2 def hit(self, entry, color): self.currentHits += 1 # Create a brush to paint on the texture splat = PNMImage("../data/Splat.png") splat = splat * LColorf(color[0], color[1], color[2], 1.0) self.colorBrush = PNMBrush.makeImage(splat, 6, 6, 1) self.paintAvatar(entry) if self.currentHits >= self.maxHits: base.messenger.send("GameOver-player%d" % id(self)) self.isOut = True def __paint(self, s, t): """ Paints a point on the avatar at texture coordinates (s, t). """ x = (s * self.p.getXSize()) y = ((1.0 - t) * self.p.getYSize()) # Draw in color directly on the avatar p1 = PNMPainter(self.p) p1.setPen(self.colorBrush) p1.drawPoint(x, y) self.tex.load(self.p) self.tex.setWrapU(self.tex.WMClamp) self.tex.setWrapV(self.tex.WMClamp) self.paintDirty = True def paintAvatar(self, entry): """ Paints onto an avatar. Returns true on success, false on failure (because there are no avatar pixels under the mouse, for instance). """ # First, we have to render the avatar in its false-color # image, to determine which part of its texture is under the # mouse. if not self.avbuf: return False #mpos = base.mouseWatcherNode.getMouse() mpos = entry.getSurfacePoint(self.player) ppos = entry.getSurfacePoint(render) self.player.showThrough(BitMask32.bit(1)) self.avbuf.setActive(True) base.graphicsEngine.renderFrame() self.player.show(BitMask32.bit(1)) self.avbuf.setActive(False) # Now we have the rendered image in self.avbufTex. if not self.avbufTex.hasRamImage(): print "Weird, no image in avbufTex." return False p = PNMImage() self.avbufTex.store(p) ix = int((1 + mpos.getX()) * p.getXSize() * 0.5) iy = int((1 - mpos.getY()) * p.getYSize() * 0.5) x = 1 if ix >= 0 and ix < p.getXSize() and iy >= 0 and iy < p.getYSize(): s = p.getBlue(ix, iy) t = p.getGreen(ix, iy) x = p.getRed(ix, iy) if x > 0.5: # Off the avatar. return False # At point (s, t) on the avatar's map. self.__paint(s, t) return True def move(self, task): if self is None: return task.done if self.userControlled: if not base.mouseWatcherNode.hasMouse(): return task.cont self.pointer = base.win.getPointer(0) mouseX = self.pointer.getX() mouseY = self.pointer.getY() if base.win.movePointer(0, self.winXhalf, self.winYhalf): p = self.TorsorControl.getP() + (mouseY - self.winYhalf) * self.mouseSpeedY if p <-80: p = -80 elif p > 90: p = 90 self.TorsorControl.setP(p) h = self.player.getH() - (mouseX - self.winXhalf) * self.mouseSpeedX if h <-360: h = 360 elif h > 360: h = -360 self.player.setH(h) else: self.TorsorControl.setP(self.AIP) self.player.setH(self.AIH) forward = self.keyMap["forward"] != 0 backward = self.keyMap["backward"] != 0 if self.keyMap["left"] != 0: if self.player.getCurrentAnim() != "Sidestep" and not (forward or backward): self.player.loop("Sidestep") self.player.setPlayRate(5, "Sidestep") self.player.setX(self.player, self.movespeed * globalClock.getDt()) elif self.keyMap["right"] != 0: if self.player.getCurrentAnim() != "Sidestep" and not (forward or backward): self.player.loop("Sidestep") self.player.setPlayRate(5, "Sidestep") self.player.setX(self.player, -self.movespeed * globalClock.getDt()) else: self.player.stop("Sidestep") if forward: if self.player.getCurrentAnim() != "Run": self.player.loop("Run") self.player.setPlayRate(5, "Run") self.player.setY(self.player, -self.movespeed * globalClock.getDt()) elif backward: if self.player.getCurrentAnim() != "Run": self.player.loop("Run") self.player.setPlayRate(-5, "Run") self.player.setY(self.player, self.movespeed * globalClock.getDt()) else: self.player.stop("Run") if not (self.keyMap["left"] or self.keyMap["right"] or self.keyMap["forward"] or self.keyMap["backward"] or self.player.getCurrentAnim() == "Idle"): self.player.loop("Idle") self.footstep.stop() else: self.footstep.play() return task.cont
def build(self, exit_behav): self.drv_info = self.props.gameprops.drivers_info menu_props = self.menu_props widgets = [Text(_('Select the driver'), pos=(0, .8), **menu_props.text_args)] t_a = self.menu_props.text_args.copy() del t_a['scale'] self.name = Text(_('Write your name:'), pos=(-.1, .6), scale=.06, align='right', wordwrap=128, **t_a) self.drivers = [] for row, col in product(range(2), range(4)): idx = col + row * 4 drv_btn = ImgBtn( scale=(.24, .24), pos=(-.75 + col * .5, .3 - row * .64), frame_col=(0, 0, 0, 0), img=self.props.gameprops.drivers_img.path % idx, cmd=self.on_click, extra_args=[idx], **self.menu_props.imgbtn_args) name = Text( '', pos=(-.75 + col * .5, .01 - row * .64), scale=.046, **t_a) drv_btn._name_txt = name widgets += [drv_btn, name] self.drivers += [widgets[-2]] sign = lambda pos_x: '\1green\1+\2' if pos_x > 0 else '' psign = lambda pos_x, sgn=sign: '+' if pos_x == 0 else sgn(pos_x) def ppcol(x): return '\1green\1%s\2' % x if x > 0 else '\1red\1%s\2' % x pcol = lambda x: x if x == 0 else ppcol(x) lab_lst = [(_('adherence'), .09), (_('speed'), .21), (_('stability'), .15)] widgets += list(map( lambda lab_def: self._add_lab(*(lab_def + (row, col))), lab_lst)) txt_lst = [(self.drv_info[idx - 1].adherence, .09), (self.drv_info[idx - 1].speed, .21), (self.drv_info[idx - 1].stability, .15)] widgets += list(map( lambda txt_def: self._add_txt( *txt_def + (psign, pcol, col, row)), txt_lst)) self.sel_drv_img = Img( self.props.gameprops.cars_img % self.mediator.car, parent=base.a2dBottomLeft, pos=(.3, .4), scale=.28) instr_txt = _( 'If you use the keyboard, press FIRE to edit the field, then ' "ENTER when you're done") instr = Text(instr_txt, pos=(1.4, .6), scale=.042, wordwrap=16, **t_a) widgets += [self.sel_drv_img, self.name, instr] self.add_widgets(widgets) ffilterpath = self.eng.curr_path + 'yyagl/assets/shaders/filter.vert' with open(ffilterpath) as ffilter: vert = ffilter.read() shader = load_shader(vert, frag) if shader: self.sel_drv_img.set_shader(shader) self.sel_drv_img.set_transparent() self.t_s = TextureStage('ts') self.t_s.set_mode(TextureStage.MDecal) empty_img = PNMImage(1, 1) empty_img.add_alpha() empty_img.alpha_fill(0) tex = Texture() tex.load(empty_img) self.sel_drv_img.set_texture(self.t_s, tex) ThanksPageGui.build(self, exit_behav=exit_behav)
def build(self): self.drv_info = self.props.gameprops.drivers_info menu_props = self.menu_props widgets = [Text(_('Select the drivers'), pos=(0, .91), **menu_props.text_args)] t_a = self.menu_props.text_args.copy() del t_a['scale'] self.name = Text(_('Write your names:'), pos=(-.1, .7), scale=.06, align='right', wordwrap=128, **t_a) self.drivers = [] for row, col in product(range(2), range(4)): idx = col + row * 4 drv_btn = ImgBtn( scale=(.24, .24), pos=(-.75 + col * .5, .1 - row * .64), frame_col=(0, 0, 0, 0), img=self.props.gameprops.drivers_img.path % idx, cmd=self.on_click, extra_args=[idx], **self.menu_props.imgbtn_args) name = Text( '', pos=(-.75 + col * .5, -.19 - row * .64), scale=.046, **t_a) drv_btn._name_txt = name widgets += [drv_btn, name] self.drivers += [widgets[-2]] sign = lambda pos_x: '\1green\1+\2' if pos_x > 0 else '' psign = lambda pos_x, sgn=sign: '+' if pos_x == 0 else sgn(pos_x) def ppcol(x): return '\1green\1%s\2' % x if x > 0 else '\1red\1%s\2' % x pcol = lambda x: x if x == 0 else ppcol(x) lab_lst = [(_('adherence'), -.11), (_('speed'), .01), (_('stability'), -.05)] widgets += list(map( lambda lab_def: self._add_lab(*(lab_def + (row, col))), lab_lst)) txt_lst = [(self.drv_info[idx - 1].adherence, -.11), (self.drv_info[idx - 1].speed, .01), (self.drv_info[idx - 1].stability, -.05)] widgets += list(map( lambda txt_def: self._add_txt( *txt_def + (psign, pcol, col, row)), txt_lst)) self.sel_drv_img = [] self.tss = [] instr_txt = _( 'If you use the keyboard, press FIRE to edit the field, then ' "ENTER when you're done. Other players can't move while someone" 'is writing (since, with keyboards, some letters may be bound to ' 'movements).') instr = Text(instr_txt, pos=(1.28, .8), scale=.042, wordwrap=24, **t_a) widgets += [self.name, instr] for i, car in enumerate(self.mediator.cars): self.sel_drv_img += [Img( self.props.gameprops.cars_img % car, parent=base.a2dBottomLeft, pos=(.3, 1.74 - i * .46), scale=.22)] widgets += [self.sel_drv_img[-1]] ffilterpath = self.eng.curr_path + 'yyagl/assets/shaders/filter.vert' with open(ffilterpath) as ffilter: vert = ffilter.read() shader = load_shader(vert, frag) if shader: self.sel_drv_img[-1].set_shader(shader) self.sel_drv_img[-1].set_transparent() self.tss += [TextureStage('ts')] self.tss[-1].set_mode(TextureStage.MDecal) empty_img = PNMImage(1, 1) empty_img.add_alpha() empty_img.alpha_fill(0) tex = Texture() tex.load(empty_img) self.sel_drv_img[-1].set_texture(self.tss[-1], tex) self.ents = [Entry( scale=.06, pos=(0, .8 - .12 * i), entry_font=menu_props.font, width=12, frame_col=menu_props.btn_col, initial_text=self.props.gameprops.player_name or _('your name'), text_fg=menu_props.text_active_col) for i in range(len(self.mediator.cars))] self.add_widgets(self.ents) self.add_widgets(widgets) ThanksPageGui.build(self, exit_behav=False) self.update_tsk = taskMgr.add(self.update_text, 'update text') self.enable_buttons(False)
class HtmlView(DirectObject): notify = DirectNotifyGlobal.directNotify.newCategory('HtmlView') useHalfTexture = base.config.GetBool('news-half-texture', 0) def __init__(self, parent = aspect2d): global GlobalWebcore self.parent_ = parent self.mx = 0 self.my = 0 self.htmlFile = 'index.html' self.transparency = False if GlobalWebcore: pass else: GlobalWebcore = AwWebCore(AwWebCore.LOGVERBOSE, True, AwWebCore.PFBGRA) GlobalWebcore.setBaseDirectory('.') for errResponse in xrange(400, 600): GlobalWebcore.setCustomResponsePage(errResponse, 'error.html') self.webView = GlobalWebcore.createWebView(WEB_WIDTH, WEB_HEIGHT, self.transparency, False, 70) frameName = '' inGameNewsUrl = self.getInGameNewsUrl() self.imgBuffer = array.array('B') for i in xrange(WEB_WIDTH * WEB_HEIGHT): self.imgBuffer.append(0) self.imgBuffer.append(0) self.imgBuffer.append(0) self.imgBuffer.append(255) if self.useHalfTexture: self.leftBuffer = array.array('B') for i in xrange(WEB_HALF_WIDTH * WEB_HEIGHT): self.leftBuffer.append(0) self.leftBuffer.append(0) self.leftBuffer.append(0) self.leftBuffer.append(255) self.rightBuffer = array.array('B') for i in xrange(WEB_HALF_WIDTH * WEB_HEIGHT): self.rightBuffer.append(0) self.rightBuffer.append(0) self.rightBuffer.append(0) self.rightBuffer.append(255) self.setupTexture() if self.useHalfTexture: self.setupHalfTextures() self.accept('mouse1', self.mouseDown, [AwWebView.LEFTMOUSEBTN]) self.accept('mouse3', self.mouseDown, [AwWebView.RIGHTMOUSEBTN]) self.accept('mouse1-up', self.mouseUp, [AwWebView.LEFTMOUSEBTN]) self.accept('mouse3-up', self.mouseUp, [AwWebView.RIGHTMOUSEBTN]) def getInGameNewsUrl(self): result = base.config.GetString('fallback-news-url', 'http://cdn.toontown.disney.go.com/toontown/en/gamenews/') override = base.config.GetString('in-game-news-url', '') if override: self.notify.info('got an override url, using %s for in a game news' % override) result = override else: try: launcherUrl = base.launcher.getValue('GAME_IN_GAME_NEWS_URL', '') if launcherUrl: result = launcherUrl self.notify.info('got GAME_IN_GAME_NEWS_URL from launcher using %s' % result) else: self.notify.info('blank GAME_IN_GAME_NEWS_URL from launcher, using %s' % result) except: self.notify.warning('got exception getting GAME_IN_GAME_NEWS_URL from launcher, using %s' % result) return result def setupTexture(self): cm = CardMaker('quadMaker') 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() self.quad = NodePath(card) self.quad.reparentTo(self.parent_) self.guiTex = Texture('guiTex') self.guiTex.setupTexture(Texture.TT2dTexture, WEB_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) self.guiTex.setMinfilter(Texture.FTLinear) self.guiTex.setKeepRamImage(True) self.guiTex.makeRamImage() self.guiTex.setWrapU(Texture.WMRepeat) self.guiTex.setWrapV(Texture.WMRepeat) ts = TextureStage('webTS') self.quad.setTexture(ts, self.guiTex) self.quad.setTexScale(ts, 1.0, -1.0) self.quad.setTransparency(0) self.quad.setTwoSided(True) self.quad.setColor(1.0, 1.0, 1.0, 1.0) self.calcMouseLimits() def setupHalfTextures(self): self.setupLeftTexture() self.setupRightTexture() self.fullPnmImage = PNMImage(WEB_WIDTH, WEB_HEIGHT, 4) self.leftPnmImage = PNMImage(WEB_HALF_WIDTH, WEB_HEIGHT, 4) self.rightPnmImage = PNMImage(WEB_HALF_WIDTH, WEB_HEIGHT, 4) def setupLeftTexture(self): cm = CardMaker('quadMaker') cm.setColor(1.0, 1.0, 1.0, 1.0) aspect = base.camLens.getAspectRatio() htmlWidth = 2.0 * aspect * WEB_WIDTH / float(WIN_WIDTH) htmlHeight = 2.0 * float(WEB_HEIGHT) / float(WIN_HEIGHT) cm.setFrame(-htmlWidth / 2.0, 0, -htmlHeight / 2.0, htmlHeight / 2.0) card = cm.generate() self.leftQuad = NodePath(card) self.leftQuad.reparentTo(self.parent_) self.leftGuiTex = Texture('guiTex') self.leftGuiTex.setupTexture(Texture.TT2dTexture, WEB_HALF_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) self.leftGuiTex.setKeepRamImage(True) self.leftGuiTex.makeRamImage() self.leftGuiTex.setWrapU(Texture.WMClamp) self.leftGuiTex.setWrapV(Texture.WMClamp) ts = TextureStage('leftWebTS') self.leftQuad.setTexture(ts, self.leftGuiTex) self.leftQuad.setTexScale(ts, 1.0, -1.0) self.leftQuad.setTransparency(0) self.leftQuad.setTwoSided(True) self.leftQuad.setColor(1.0, 1.0, 1.0, 1.0) def setupRightTexture(self): cm = CardMaker('quadMaker') cm.setColor(1.0, 1.0, 1.0, 1.0) aspect = base.camLens.getAspectRatio() htmlWidth = 2.0 * aspect * WEB_WIDTH / float(WIN_WIDTH) htmlHeight = 2.0 * float(WEB_HEIGHT) / float(WIN_HEIGHT) cm.setFrame(0, htmlWidth / 2.0, -htmlHeight / 2.0, htmlHeight / 2.0) card = cm.generate() self.rightQuad = NodePath(card) self.rightQuad.reparentTo(self.parent_) self.rightGuiTex = Texture('guiTex') self.rightGuiTex.setupTexture(Texture.TT2dTexture, WEB_HALF_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) self.rightGuiTex.setKeepRamImage(True) self.rightGuiTex.makeRamImage() self.rightGuiTex.setWrapU(Texture.WMClamp) self.rightGuiTex.setWrapV(Texture.WMClamp) ts = TextureStage('rightWebTS') self.rightQuad.setTexture(ts, self.rightGuiTex) self.rightQuad.setTexScale(ts, 1.0, -1.0) self.rightQuad.setTransparency(0) self.rightQuad.setTwoSided(True) self.rightQuad.setColor(1.0, 1.0, 1.0, 1.0) def calcMouseLimits(self): ll = Point3() ur = Point3() self.quad.calcTightBounds(ll, ur) self.notify.debug('ll=%s ur=%s' % (ll, ur)) offset = self.quad.getPos(aspect2d) self.notify.debug('offset = %s ' % offset) ll.setZ(ll.getZ() + offset.getZ()) ur.setZ(ur.getZ() + offset.getZ()) self.notify.debug('new LL=%s, UR=%s' % (ll, ur)) relPointll = self.quad.getRelativePoint(aspect2d, ll) self.notify.debug('relPoint = %s' % relPointll) self.mouseLL = (aspect2d.getScale()[0] * ll[0], aspect2d.getScale()[2] * ll[2]) self.mouseUR = (aspect2d.getScale()[0] * ur[0], aspect2d.getScale()[2] * ur[2]) self.notify.debug('original mouseLL=%s, mouseUR=%s' % (self.mouseLL, self.mouseUR)) def writeTex(self, filename = 'guiText.png'): self.notify.debug('writing texture') self.guiTex.generateRamMipmapImages() self.guiTex.write(filename) def toggleRotation(self): if self.interval.isPlaying(): self.interval.finish() else: self.interval.loop() def mouseDown(self, button): messenger.send('wakeup') self.webView.injectMouseDown(button) def mouseUp(self, button): self.webView.injectMouseUp(button) def reload(self): pass def zoomIn(self): self.webView.zoomIn() def zoomOut(self): self.webView.zoomOut() def toggleTransparency(self): self.transparency = not self.transparency self.webView.setTransparent(self.transparency) def update(self, task): if base.mouseWatcherNode.hasMouse(): x, y = self._translateRelativeCoordinates(base.mouseWatcherNode.getMouseX(), base.mouseWatcherNode.getMouseY()) if self.mx - x != 0 or self.my - y != 0: self.webView.injectMouseMove(x, y) self.mx, self.my = x, y if self.webView.isDirty(): self.webView.render(self.imgBuffer.buffer_info()[0], WEB_WIDTH * 4, 4) Texture.setTexturesPower2(2) textureBuffer = self.guiTex.modifyRamImage() textureBuffer.setData(self.imgBuffer.tostring()) if self.useHalfTexture: self.guiTex.store(self.fullPnmImage) self.leftPnmImage.copySubImage(self.fullPnmImage, 0, 0, 0, 0, WEB_HALF_WIDTH, WEB_HEIGHT) self.rightPnmImage.copySubImage(self.fullPnmImage, 0, 0, WEB_HALF_WIDTH, 0, WEB_HALF_WIDTH, WEB_HEIGHT) self.leftGuiTex.load(self.leftPnmImage) self.rightGuiTex.load(self.rightPnmImage) self.quad.hide() Texture.setTexturesPower2(1) GlobalWebcore.update() return Task.cont def _translateRelativeCoordinates(self, x, y): sx = int((x - self.mouseLL[0]) / (self.mouseUR[0] - self.mouseLL[0]) * WEB_WIDTH_PIXELS) sy = WEB_HEIGHT_PIXELS - int((y - self.mouseLL[1]) / (self.mouseUR[1] - self.mouseLL[1]) * WEB_HEIGHT_PIXELS) return (sx, sy) def unload(self): self.ignoreAll() self.webView.destroy() self.webView = None return def onCallback(self, name, args): if name == 'requestFPS': pass def onBeginNavigation(self, url, frameName): pass def onBeginLoading(self, url, frameName, statusCode, mimeType): pass def onFinishLoading(self): self.notify.debug('finished loading') def onReceiveTitle(self, title, frameName): pass def onChangeTooltip(self, tooltip): pass def onChangeCursor(self, cursor): pass def onChangeKeyboardFocus(self, isFocused): pass def onChangeTargetURL(self, url): pass
class MazeMapGui(DirectFrame): notify = directNotify.newCategory('MazeMapGui') def __init__(self, mazeCollTable, maskResolution=None, radiusRatio=None, bgColor=(0.8, 0.8, 0.8), fgColor=(0.5, 0.5, 0.5, 1.0)): DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) self.hide() self._bgColor = bgColor self._fgColor = fgColor self._mazeCollTable = mazeCollTable self._mazeWidth = len(self._mazeCollTable[0]) self._mazeHeight = len(self._mazeCollTable) self._maskResolution = maskResolution or DEFAULT_MASK_RESOLUTION if radiusRatio is None: self._radius = self._maskResolution * DEFAULT_RADIUS_RATIO else: self._radius = self._maskResolution * radiusRatio self._revealedCells = [] for y in range(self._mazeHeight): self._revealedCells.append([]) for u in range(self._mazeWidth): self._revealedCells[y].append(False) self._revealFunctions = { MazeRevealType.SmoothCircle: self._revealSmoothCircle, MazeRevealType.HardCircle: self._revealHardCircle, MazeRevealType.Square: self._revealSquare } self._revealFunction = MAZE_REVEAL_TYPE self.map = self._createMapTextureCard() self.map.reparentTo(self) self.maskedLayer = self.attachNewNode('maskedLayer') self.mask = self._createMaskTextureCard() self.mask.reparentTo(self) self.visibleLayer = self.attachNewNode('visibleLayer') self._laffMeterModel = loader.loadModel( 'phase_3/models/gui/laff_o_meter') self._toon2marker = {} return def _createMapTextureCard(self): mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION) mapImage.fill(*self._bgColor) fgColor = VBase4D(*self._fgColor) for x in range(self._mazeHeight): for y in range(self._mazeWidth): if self._mazeCollTable[y][x] == 1: ax = float(x) / self._mazeWidth * MAP_RESOLUTION invertedY = self._mazeHeight - 1 - y ay = float(invertedY) / self._mazeHeight * MAP_RESOLUTION self._drawSquare(mapImage, int(ax), int(ay), 10, fgColor) mapTexture = Texture('mapTexture') mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) mapTexture.setMinfilter(Texture.FTLinear) mapTexture.load(mapImage) mapTexture.setWrapU(Texture.WMClamp) mapTexture.setWrapV(Texture.WMClamp) mapImage.clear() del mapImage cm = CardMaker('map_cardMaker') cm.setFrame(-1.0, 1.0, -1.0, 1.0) map = self.attachNewNode(cm.generate()) map.setTexture(mapTexture, 1) return map def _createMaskTextureCard(self): self._maskImage = PNMImage(self._maskResolution, self._maskResolution, 4) for x in range(self._maskResolution): for y in range(self._maskResolution): self._maskImage.setXelA(x, y, 0, 0, 0, 1) self.maskTexture = Texture('maskTexture') self.maskTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) self.maskTexture.setMinfilter(Texture.FTLinear) self.maskTexture.setWrapU(Texture.WMClamp) self.maskTexture.setWrapV(Texture.WMClamp) self.maskTexture.load(self._maskImage) base.graphicsEngine.renderFrame() cm = CardMaker('mask_cardMaker') cm.setFrame(-1.1, 1.1, -1.1, 1.1) mask = self.attachNewNode(cm.generate()) mask.setTexture(self.maskTexture, 1) mask.setTransparency(1) return mask def _drawSquare(self, image, ulx, uly, size, color): x = int(ulx) while x <= ulx + size: y = int(uly) while y <= uly + size: if x > 0 and y > 0 and x < image.getXSize( ) and y < image.getYSize(): image.setXelA(x, y, color) y += 1 x += 1 def destroy(self): del self._mazeCollTable del self._maskResolution del self._radius del self._revealedCells del self._revealFunctions del self._revealFunction self.map.removeNode() del self.map self.mask.removeNode() del self.mask self.maskedLayer.removeNode() del self.maskedLayer self.visibleLayer.removeNode() del self.visibleLayer self._maskImage.clear() del self._maskImage self.maskTexture.clear() del self.maskTexture self._laffMeterModel.removeNode() del self._laffMeterModel DirectFrame.destroy(self) def _revealSmoothCircle(self, x, y, center): length = (Vec2(x, y) - center).length() goalAlpha = max(0.0, length / float(self._radius) - 0.5) self._maskImage.setXelA( x, y, VBase4D(0.0, 0.0, 0.0, min(self._maskImage.getAlpha(x, y), goalAlpha * 2.0))) def _revealHardCircle(self, x, y, center): length = (Vec2(x, y) - center).length() if length <= self._radius: self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0)) def _revealSquare(self, x, y, center): self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0)) def _drawHole(self, x, y): center = Vec2(x, y) ul = center - Vec2(self._radius, self._radius) lr = center + Vec2(self._radius, self._radius) x = int(ul[0]) while x <= lr[0]: y = int(ul[1]) while y <= lr[1]: if x > 0 and y > 0 and x < self._maskResolution and y < self._maskResolution: self._revealFunctions[self._revealFunction](x, y, center) y += 1 x += 1 self.maskTexture.load(self._maskImage) self.mask.setTexture(self.maskTexture, 1) def _createSimpleMarker(self, size, color=(1, 1, 1)): halfSize = size * 0.5 cm = CardMaker('mazemap_simple_marker') cm.setFrame(-halfSize, halfSize, -halfSize, halfSize) markerNP = self.maskedLayer.attachNewNode(cm.generate()) markerNP.setColor(*color) return markerNP def tile2gui(self, x, y): y = self._mazeHeight - y cellWidth = self._maskResolution / self._mazeWidth cellHeight = self._maskResolution / self._mazeHeight ax = float(x) / self._mazeWidth * self._maskResolution ax += cellWidth ay = float(y) / self._mazeHeight * self._maskResolution ay += cellHeight return (ax, ay) def gui2pos(self, x, y): return (x / self._maskResolution * 2.0 - 0.97, 0, y / self._maskResolution * -2.0 + 1.02) def _getToonMarker(self, toon): hType = toon.style.getType() if hType == 'rabbit': hType = 'bunny' return self._laffMeterModel.find('**/' + hType + 'head') def addToon(self, toon, tX, tY): marker = NodePath('toon_marker-%i' % toon.doId) marker.reparentTo(self) self._getToonMarker(toon).copyTo(marker) marker.setColor(toon.style.getHeadColor()) if toon.isLocal(): marker.setScale(0.07) else: marker.setScale(0.05) marker.flattenStrong() marker.setPos(*self.gui2pos(*self.tile2gui(tX, tY))) self._toon2marker[toon] = marker def removeToon(self, toon): if toon not in self._toon2marker: return self._toon2marker[toon].removeNode() del self._toon2marker[toon] def updateToon(self, toon, tX, tY): if toon not in self._toon2marker: return x, y = self.tile2gui(tX, tY) self._toon2marker[toon].setPos(*self.gui2pos(x, y)) if tY < 0 or tY >= len(self._revealedCells): self.notify.warning('updateToon earlying out:') self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY)) self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells), )) if len(self._revealedCells) > 0: self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]), )) return if tX < 0 or tX >= len(self._revealedCells[tY]): self.notify.warning('updateToon earlying out:') self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY)) self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells), )) if tY < len(self._revealedCells): self.notify.warning('len(_revealedCells[tY]): %s' % (len(self._revealedCells[tY]), )) elif len(self._revealedCells) > 0: self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]), )) return if not self._revealedCells[tY][tX]: self._drawHole(x, y) self._revealedCells[tY][tX] = True def revealCell(self, x, y): ax, ay = self.tile2gui(x, y) if not self._revealedCells[y][x]: self._drawHole(ax, ay) self._revealedCells[y][x] = True def revealAll(self): for x in range(self._maskResolution): for y in range(self._maskResolution): self._maskImage.setXelA(x, y, 0, 0, 0, 0) self.revealCell(0, 0) def reset(self): for x in range(self._maskResolution): for y in range(self._maskResolution): self._maskImage.setXelA(x, y, 0, 0, 0, 1)
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 Typist(object): TARGETS = { 'paper': { 'model': 'paper', 'textureRoot': 'Front', 'scale': Point3(0.85, 0.85, 1), 'hpr': Point3(0, 0, 0), } } def __init__(self, base, typewriterNP, underDeskClip, sounds): self.base = base self.sounds = sounds self.underDeskClip = underDeskClip self.typeIndex = 0 self.typewriterNP = typewriterNP self.rollerAssemblyNP = typewriterNP.find("**/roller assembly") assert self.rollerAssemblyNP self.rollerNP = typewriterNP.find("**/roller") assert self.rollerNP self.carriageNP = typewriterNP.find("**/carriage") assert self.carriageNP self.baseCarriagePos = self.carriageNP.getPos() self.carriageBounds = self.carriageNP.getTightBounds() self.font = base.loader.loadFont('Harting.ttf', pointSize=32) self.pnmFont = PNMTextMaker(self.font) self.fontCharSize, _, _ = fonts.measureFont(self.pnmFont, 32) print "font char size: ", self.fontCharSize self.pixelsPerLine = int(round(self.pnmFont.getLineHeight())) self.target = None """ panda3d.core.NodePath """ self.targetRoot = None """ panda3d.core.NodePath """ self.paperY = 0.0 """ range from 0 to 1 """ self.paperX = 0.0 """ range from 0 to 1 """ self.createRollerBase() self.tex = None self.texImage = None self.setupTexture() self.scheduler = Scheduler() task = self.base.taskMgr.add(self.tick, 'timerTask') task.setDelay(0.01) def tick(self, task): self.scheduler.tick(globalClock.getRealTime()) return task.cont def setupTexture(self): """ This is the overlay/decal/etc. which contains the typed characters. The texture size and the font size are currently tied together. :return: """ self.texImage = PNMImage(1024, 1024) self.texImage.addAlpha() self.texImage.fill(1.0) self.texImage.alphaFill(1.0) self.tex = Texture('typing') self.tex.setMagfilter(Texture.FTLinear) self.tex.setMinfilter(Texture.FTLinear) self.typingStage = TextureStage('typing') self.typingStage.setMode(TextureStage.MModulate) self.tex.load(self.texImage) # ensure we can quickly update subimages self.tex.setKeepRamImage(True) # temp for drawing chars self.chImage = PNMImage(*self.fontCharSize) def drawCharacter(self, ch, px, py): """ Draw a character onto the texture :param ch: :param px: paperX :param py: paperY :return: the paper-relative size of the character """ h = self.fontCharSize[1] if ch != ' ': # position -> pixel, applying margins x = int(self.tex.getXSize() * (px * 0.8 + 0.1)) y = int(self.tex.getYSize() * (py * 0.8 + 0.1)) # always draw onto the paper, to capture # incremental character overstrikes self.pnmFont.generateInto(ch, self.texImage, x, y) if False: #print ch,"to",x,y,"w=",g.getWidth() self.tex.load(self.texImage) else: # copy an area (presumably) encompassing the character g = self.pnmFont.getGlyph(ord(ch)) cx, cy = self.fontCharSize # a glyph is minimally sized and "moves around" in its text box # (think ' vs. ,), so it has been drawn somewhere relative to # the 'x' and 'y' we wanted. x += g.getLeft() y -= g.getTop() self.chImage.copySubImage( self.texImage, 0, 0, # from x, y, # to cx, cy # size ) self.tex.loadSubImage(self.chImage, x, y) # toggle for a typewriter that uses non-proportional spacing #w = self.paperCharWidth(g.getWidth()) w = self.paperCharWidth() else: w = self.paperCharWidth() return w, h def start(self): self.target = None self.setTarget('paper') self.hookKeyboard() def createRollerBase(self): """ The paper moves such that it is tangent to the roller. This nodepath keeps a coordinate space relative to that, so that the paper can be positioned from (0,0,0) to (0,0,1) to "roll" it along the roller. """ bb = self.rollerNP.getTightBounds() #self.rollerNP.showTightBounds() self.paperRollerBase = self.rollerAssemblyNP.attachNewNode( 'rollerBase') self.paperRollerBase.setHpr(0, -20, 0) print "roller:", bb rad = abs(bb[0].y - bb[1].y) / 2 center = Vec3(-(bb[0].x + bb[1].x) / 2 - 0.03, (bb[0].y - bb[1].y) / 2, (bb[0].z + bb[1].z) / 2) self.paperRollerBase.setPos(center) def setTarget(self, name): if self.target: self.target.removeNode() # load and transform the model target = self.TARGETS[name] self.target = self.base.loader.loadModel(target['model']) #self.target.setScale(target['scale']) self.target.setHpr(target['hpr']) # put it in the world self.target.reparentTo(self.paperRollerBase) rbb = self.rollerNP.getTightBounds() tbb = self.target.getTightBounds() rs = (rbb[1] - rbb[0]) ts = (tbb[1] - tbb[0]) self.target.setScale(rs.x / ts.x, 1, 1) # apply the texture self.targetRoot = self.target if 'textureRoot' in target: self.targetRoot = self.target.find("**/" + target['textureRoot']) assert self.targetRoot self.targetRoot.setTexture(self.typingStage, self.tex) #self.setupTargetClip() # reset self.paperX = self.paperY = 0. newPos = self.calcPaperPos(self.paperY) self.target.setPos(newPos) self.moveCarriage() def setupTargetClip(self): """ The target is fed in to the typewriter but until we invent "geom curling", it shouldn't be visible under the typewriter under the desk. The @underDeskClip node has a world-relative bounding box, which we can convert to the target-relative bounding box, and pass to a shader that can clip the nodes. """ shader = Shader.make( Shader.SLGLSL, """ #version 120 attribute vec4 p3d_MultiTexCoord0; attribute vec4 p3d_MultiTexCoord1; void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = p3d_MultiTexCoord0; gl_TexCoord[1] = p3d_MultiTexCoord1; } """, """ #version 120 uniform sampler2D baseTex; uniform sampler2D charTex; const vec4 zero = vec4(0, 0, 0, 0); const vec4 one = vec4(1, 1, 1, 1); const vec4 half = vec4(0.5, 0.5, 0.5, 0); void main() { vec4 baseColor = texture2D(baseTex, gl_TexCoord[0].st); vec4 typeColor = texture2D(charTex, gl_TexCoord[1].st); gl_FragColor = baseColor * typeColor; }""") self.target.setShader(shader) baseTex = self.targetRoot.getTexture() print "Base Texture:", baseTex self.target.setShaderInput("baseTex", baseTex) self.target.setShaderInput("charTex", self.tex) def hookKeyboard(self): """ Hook events so we can respond to keypresses. """ self.base.buttonThrowers[0].node().setKeystrokeEvent('keystroke') self.base.accept('keystroke', self.schedTypeCharacter) self.base.accept('backspace', self.schedBackspace) self.base.accept('arrow_up', lambda: self.schedAdjustPaper(-5)) self.base.accept('arrow_up-repeat', lambda: self.schedAdjustPaper(-1)) self.base.accept('arrow_down', lambda: self.schedAdjustPaper(5)) self.base.accept('arrow_down-repeat', lambda: self.schedAdjustPaper(1)) self.base.accept('arrow_left', lambda: self.schedAdjustCarriage(-1)) self.base.accept('arrow_left-repeat', lambda: self.schedAdjustCarriage(-1)) self.base.accept('arrow_right', lambda: self.schedAdjustCarriage(1)) self.base.accept('arrow_right-repeat', lambda: self.schedAdjustCarriage(1)) def paperCharWidth(self, pixels=None): if not pixels: pixels = self.fontCharSize[0] return float(pixels) / self.tex.getXSize() def paperLineHeight(self): return float(self.fontCharSize[1] * 1.2) / self.tex.getYSize() def schedScroll(self): if self.scheduler.isQueueEmpty(): self.schedRollPaper(1) self.schedResetCarriage() def schedBackspace(self): if self.scheduler.isQueueEmpty(): def doit(): if self.paperX > 0: self.schedAdjustCarriage(-1) self.scheduler.schedule(0.01, doit) def createMoveCarriageInterval(self, newX, curX=None): if curX is None: curX = self.paperX here = self.calcCarriage(curX) there = self.calcCarriage(newX) posInterval = LerpPosInterval(self.carriageNP, abs(newX - curX), there, startPos=here, blendType='easeIn') posInterval.setDoneEvent('carriageReset') def isReset(): self.paperX = newX self.base.acceptOnce('carriageReset', isReset) return posInterval def schedResetCarriage(self): if self.paperX > 0.1: self.sounds['pullback'].play() invl = self.createMoveCarriageInterval(0) self.scheduler.scheduleInterval(0, invl) def calcCarriage(self, paperX): """ Calculate where the carriage should be offset based on the position on the paper :param paperX: 0...1 :return: pos for self.carriageNP """ x = (0.5 - paperX) * 0.69 * 0.8 + 0.01 bb = self.carriageBounds return self.baseCarriagePos + Point3(x * (bb[1].x - bb[0].x), 0, 0) def moveCarriage(self): pos = self.calcCarriage(self.paperX) self.carriageNP.setPos(pos) def schedMoveCarriage(self, curX, newX): if self.scheduler.isQueueEmpty(): #self.scheduler.schedule(0.1, self.moveCarriage) invl = self.createMoveCarriageInterval(newX, curX=curX) invl.start() def schedAdjustCarriage(self, bx): if self.scheduler.isQueueEmpty(): def doit(): self.paperX = max( 0.0, min(1.0, self.paperX + bx * self.paperCharWidth())) self.moveCarriage() self.scheduler.schedule(0.1, doit) def calcPaperPos(self, paperY): # center over roller, peek out a little z = paperY * 0.8 - 0.5 + 0.175 bb = self.target.getTightBounds() return Point3(-0.5, 0, z * (bb[1].z - bb[0].z)) def createMovePaperInterval(self, newY): here = self.calcPaperPos(self.paperY) there = self.calcPaperPos(newY) posInterval = LerpPosInterval(self.target, abs(newY - self.paperY), there, startPos=here, blendType='easeInOut') posInterval.setDoneEvent('scrollDone') def isDone(): self.paperY = newY self.base.acceptOnce('scrollDone', isDone) return posInterval def schedAdjustPaper(self, by): if self.scheduler.isQueueEmpty(): def doit(): self.schedRollPaper(by) self.scheduler.schedule(0.1, doit) def schedRollPaper(self, by): """ Position the paper such that @percent of it is rolled over roller :param percent: :return: """ def doit(): self.sounds['scroll'].play() newY = min(1.0, max(0.0, self.paperY + self.paperLineHeight() * by)) invl = self.createMovePaperInterval(newY) invl.start() self.scheduler.schedule(0.1, doit) def schedTypeCharacter(self, keyname): # filter for visibility if ord(keyname) == 13: self.schedScroll() elif ord(keyname) >= 32 and ord(keyname) != 127: if self.scheduler.isQueueEmpty(): curX, curY = self.paperX, self.paperY self.typeCharacter(keyname, curX, curY) def typeCharacter(self, ch, curX, curY): newX = curX w, h = self.drawCharacter(ch, curX, curY) newX += w if ch != ' ': # alternate typing sound #self.typeIndex = (self.typeIndex+1) % 3 self.typeIndex = random.randint(0, 2) self.sounds['type' + str(self.typeIndex + 1)].play() else: self.sounds['advance'].play() if newX >= 1: self.sounds['bell'].play() newX = 1 self.schedMoveCarriage(self.paperX, newX) # move first, to avoid overtype self.paperX = newX
def checkForLoad(task): #print globalClock.getAverageFrameRate() global model_queue checkQueue() try: action = load_queue.get_nowait() except Queue.Empty: action = None if EXIT_AFTER and action is None and base.num_models_loaded >= NUM_MODELS and base.quit and base.screenshot_frame > base.quit_frame: sys.exit(0) elif action is None and base.num_models_loaded >= NUM_MODELS and base.quit and base.screenshot_frame > base.quit_frame: base.render.analyze() base.quit = False if action is None: return task.cont action_type = action[0] if action_type == ActionType.LOAD_MODEL: model = action[1] print 'Queueing model for load:', model.model_json['full_path'], 'queue size', len(model_queue) model_queue.append(model) checkQueue() elif action_type == ActionType.UPDATE_TEXTURE: model = action[1] offset = action[2] data = action[3] print model.model_json['base_path'], 'needs texture updating offset', offset texpnm = PNMImage() texpnm.read(StringStream(data), 'something.jpg') newtex = Texture() newtex.load(texpnm) path_search = '**/*' + model.model_json['full_path'].replace('/', '_') + '*' np = render.find(path_search) np.setTextureOff(1) np.setTexture(newtex, 1) elif action_type == ActionType.PROGRESSIVE_ADDITION: model = action[1] refinements = action[2] path_search = '**/*' + model.model_json['full_path'].replace('/', '_') + '*' np = render.find(path_search) np = np.find("**/primitive") pnode = np.node() start = time.time() update_nodepath(pnode, refinements) end = time.time() print '---JUST UPDATED ' + model.model_json['full_path'] + ' ----' elif action_type == ActionType.QUIT: print 'Got a quit message, triggering quit flag' base.quit = True return task.cont