def loadTerrain(name, widthScale = 0.5, heightScale = 10.0): """load terrain stuff""" global app, terrain, terrainRootNetPos steerMgr = OSSteerManager.get_global_ptr() terrain = GeoMipTerrain("terrain") heightField = PNMImage(Filename(dataDir + "/heightfield.png")) terrain.set_heightfield(heightField) # sizing environmentWidthX = (heightField.get_x_size() - 1) * widthScale environmentWidthY = (heightField.get_y_size() - 1) * widthScale environmentWidth = (environmentWidthX + environmentWidthY) / 2.0 terrain.get_root().set_sx(widthScale) terrain.get_root().set_sy(widthScale) terrain.get_root().set_sz(heightScale) # set other terrain's properties blockSize, minimumLevel = (64, 0) nearPercent, farPercent = (0.1, 0.7) terrainLODmin = min(minimumLevel, terrain.get_max_level()) flattenMode = GeoMipTerrain.AFM_off terrain.set_block_size(blockSize) terrain.set_near(nearPercent * environmentWidth) terrain.set_far(farPercent * environmentWidth) terrain.set_min_level(terrainLODmin) terrain.set_auto_flatten(flattenMode) # terrain texturing textureStage0 = TextureStage("TextureStage0") textureImage = TexturePool.load_texture(Filename("terrain.png")) terrain.get_root().set_tex_scale(textureStage0, 1.0, 1.0) terrain.get_root().set_texture(textureStage0, textureImage, 1) # reparent this Terrain node path to the object node path terrain.get_root().reparent_to(steerMgr.get_reference_node_path()) terrain.get_root().set_collide_mask(mask) terrain.get_root().set_name(name) # brute force generation bruteForce = True terrain.set_bruteforce(bruteForce) # Generate the terrain terrain.generate() # check if terrain needs update or not if not bruteForce: # save the net pos of terrain root terrainRootNetPos = terrain.get_root().get_net_transform().get_pos() # Add a task to keep updating the terrain app.taskMgr.add(terrainUpdate, "terrainUpdate", appendTask=True) # return terrain.get_root()
class WorldManager(object): """A world manager for heightmapped worlds stored as XML.""" def __init__(self): """Setup this world manager.""" Logger.info("Initializing world manager...") self.terrain = None self.portals = [] self.gates = [] self.objects = [] self.scenery = RigidBodyCombiner("scenery") self.scenery_np = render.attach_new_node(self.scenery) self.is_dirty = True base.task_mgr.add(self.run_logic) Logger.info("World manager initialized.") def load_map(self, map): """Load a map.""" #Unload the current map first self.unload_map() #Locate the XML file for the map Logger.info("Loading map '{}'...".format(map)) map_file = os.path.join(map, os.path.basename(map) + ".xml") if not os.path.exists(map_file): Logger.error("Failed to load map file '{}'.".format(map_file)) return False #Load the map XML file xml = etree.parse(map_file) root = xml.getroot() for child in root: #Terrain? if child.tag == "terrain": #Validate terrain if not ("size" in child.attrib and "spawnpos" in child.attrib and "heightmap" in child.attrib): Logger.error( "Terrain section must define 'size', 'spawnpos', and 'heightmap'." ) return False #Load terrain self.size = parse_vec(child.attrib["size"], 3) self.spawnpos = parse_vec(child.attrib["spawnpos"], 2) heightmap = os.path.join(map, child.attrib["heightmap"]) self.terrain = GeoMipTerrain("Terrain") self.terrain.set_block_size(64) self.terrain.set_bruteforce(True) if not self.terrain.set_heightfield(heightmap): Logger.error("Failed to load heightmap for terrain.") self.terrain = None return False self.terrain_np = self.terrain.get_root() self.terrain_np.set_scale(self.size[0] / 512, self.size[1] / 512, self.size[2]) tex = loader.load_texture( "./data/textures/terrain/grass_tex2.png") self.terrain_np.set_texture(tex) self.terrain_np.set_tex_scale(TextureStage.get_default(), self.size[0] / 512, self.size[1] / 512) tex.set_wrap_u(Texture.WM_repeat) tex.set_wrap_v(Texture.WM_repeat) self.terrain_np.reparent_to(render) self.terrain.generate() base.camera.set_pos(self.size[0] / 2, self.size[1] / 2, self.size[2]) #Portal? elif child.tag == "portal": #Validate portal if not ("pos" in child.attrib and "destmap" in child.attrib): Logger.warning("Portal must define 'pos' and 'destmap'.") continue #Load portal pos = parse_vec(child.attrib["pos"], 3) radius = parse_float( child.attrib["radius"]) if "radius" in child.attrib else 1 destmap = child.attrib["destmap"] self.add_portal(pos, radius, destmap) #Gate? elif child.tag == "gate": #Validate gate if not ("pos" in child.attrib and "destmap" in child.attrib and "destvec" in child.attrib): Logger.warning( "Gate must define 'pos', 'destmap', and 'destvec'.") continue #Load gate pos = parse_vec(child.attrib["pos"], 3) destmap = child.attrib["destmap"] destvec = parse_vec(child.attrib["destvec"], 3) material = child.attrib[ "material"] if "material" in child.attrib else "" self.add_gate(pos, destmap, destvec, material) #Object? elif child.tag == "object": #Validate object if not ("mesh" in child.attrib and "pos" in child.attrib): Logger.warning("Object must define 'mesh' and 'pos'.") continue #Load object mesh = child.attrib["mesh"] pos = parse_vec(child.attrib["pos"], 3) rot = parse_vec(child.attrib["rot"], 3) if "rot" in child.attrib else [0, 0, 0] scale = parse_vec(child.attrib["scale"], 3) if "scale" in child.attrib else [1, 1, 1] material = child.attrib[ "material"] if "material" in child.attrib else "" sound = child.attrib["sound"] if "sound" in child.attrib else "" self.add_object(mesh, pos, rot, scale, material, sound) #Object Group? elif child.tag == "objectgroup": #Validate object group if not ("mesh" in child.attrib): Logger.warning("Object group must define 'mesh'.") continue #Load object group mesh = child.attrib["mesh"] material = child.attrib[ "material"] if "material" in child.attrib else "" self.load_object_group(child, mesh, material) #Unknown? else: Logger.warning( "Unknown tag '{}' encountered in map '{}'.".format( child.tag, map)) #Map loaded Logger.info("Map '{}' loaded.".format(map)) return True def unload_map(self): """Unload the current map.""" if self.terrain is not None: self.terrain.get_root().remove_node() self.terrain = None self.size = [0, 0] self.spawnpos = [0, 0, 0] while len(self.portals) > 0: self.del_portal(self.portals[-1]) while len(self.gates) > 0: self.del_gate(self.gates[-1]) while len(self.objects) > 0: self.del_object(self.objects[-1]) def load_object_group(self, group, mesh, material): """Load a group of objects.""" for object in group: #Validate object if not ("pos" in object.attrib): Logger.warning("Group object must define 'pos'.") continue #Load object pos = parse_vec(object.attrib["pos"], 2) rot = parse_vec(object.attrib["rot"], 1) if "rot" in object.attrib else [0, 0, 0] scale = parse_vec(object.attrib["scale"], 1) if "scale" in object.attrib else [1, 1, 1] self.add_object(mesh, pos, rot, scale, material, "") def add_portal(self, pos, radius, dest): """Add a portal to this world.""" self.portals.append(Portal(pos, radius, dest)) self.is_dirty = True Logger.info("Added portal: pos = {}, radius = {}, dest = '{}'".format( pos, radius, dest)) def del_portal(self, portal): """Remove a portal from this world.""" self.portals.remove(portal) self.is_dirty = True Logger.info("Removed portal {}".format(portal)) def add_gate(self, pos, dest, destvec, material): """Add a gate to this world.""" self.gates.append(Gate(pos, dest, destvec, material)) self.is_dirty = True Logger.info( "Added gate: pos = {}, dest = '{}', destvec = {}, material = '{}'". format(pos, dest, destvec, material)) def del_gate(self, gate): """Remove a gate from this world.""" self.gates.remove(gate) self.is_dirty = True Logger.info("Removed gate {}".format(gate)) def add_object(self, mesh, pos, rot, scale, material, sound): """Add an object to this world.""" #Handle 2 coordinate position if len(pos) == 2: pos = [pos[0], pos[1], self.get_terrain_height(pos)] #Handle single coordinate rotation if len(rot) == 1: rot = [rot[0], 0, 0] #Handle single or double coordinate scale if len(scale) == 1: scale = [scale[0], scale[0], scale[0]] elif len(scale) == 2: scale = [scale[0], scale[1], 1] #Add the object self.objects.append(Object(mesh, pos, rot, scale, material, sound)) self.is_dirty = True Logger.info( "Added object: mesh = '{}', pos = {}, rot = {}, scale = {}, material = '{}', sound = '{}'" .format(mesh, pos, rot, scale, material, sound)) def del_object(self, object): """Delete an object from this world.""" self.objects.remove(object) self.is_dirty = True Logger.info("Removed object {}".format(object)) def get_terrain_height(self, pos): """Get the height of the terrain at the given point.""" #Is the position a list of 2 elements? if isinstance(pos, list) and len(pos) == 2: return self.terrain.get_elevation( pos[0] / (self.size[0] / 512), pos[1] / (self.size[1] / 512)) * self.size[2] #Assume it is a point object else: return self.terrain.get_elevation( pos[0] / (self.size[0] / 512), pos[1] / (self.size[1] / 512)) * self.size[2] def run_logic(self, task): """Run the logic for this world manager.""" #Optimize the scenery if self.is_dirty: self.scenery.collect() self.is_dirty = False Logger.info("Optimized the scenery.") return Task.cont