def __init__(self, guid: Hex = None, texture: str = None, near_plane: float = 0.1, far_plane: float = 1.1, vertical_meters: float = None, horizontal_meters: float = None, decal_origin: Position = None, decal_orientation: list[float] = None, lod: float = 1.0): if vertical_meters is None: vertical_meters = 4.0 / 1.1 * far_plane if horizontal_meters is None: horizontal_meters = 4.0 / 1.1 * far_plane self.guid = guid self.texture = texture self.near_plane = near_plane self.far_plane = far_plane self.vertical_meters = vertical_meters self.horizontal_meters = horizontal_meters self.decal_origin = decal_origin if decal_origin is not None else Position( 0, 1, 0, None) self.decal_orientation = decal_orientation if decal_orientation is not None else [ 0.0, -1.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0 ] # default orientation, no idea what these numbers mean self.lod = lod # level of detail
def map_pos_to_node_pos(self, map_pos_x, map_pos_z): xnt = int(map_pos_x / self.TILE_SIZE) znt = int(map_pos_z / self.TILE_SIZE) node_tile = self.nodes_2d[xnt][znt] ntx = map_pos_x % self.TILE_SIZE - self.TILE_SIZE / 2 ntz = map_pos_z % self.TILE_SIZE - self.TILE_SIZE / 2 na = node_tile.turn_angle() ntxt, ntzt = self.turn(ntx, ntz, -na) nx = node_tile.offset[0] + ntxt nz = node_tile.offset[1] + ntzt node_pos = Position(nx, 10, nz, node_tile.node.guid) return node_pos
def apply_progression_node_sets(tiles, tile_size_x, tile_size_z, progression: Progression, rt: RegionTiling) -> list[Decal]: # Apply node-sets from progression steps. TerrainNodes are already created at this point and have guids. max_size_xz = max(tile_size_x * rt.num_x, tile_size_z * rt.num_z) node_tiles: list[NodeTile] = [] for col in tiles: node_tiles.extend([tile for tile in col if tile.node is not None]) for tile in node_tiles: map_norm_x = (rt.cur_x * tile_size_x + tile.x + 2 / 4) / max_size_xz # x on whole map, normalized (0-1) map_norm_z = (rt.cur_z * tile_size_z + tile.z + 2 / 4) / max_size_xz # z on whole map, normalized (0-1) progression_step = progression.choose_progression_step( map_norm_x, map_norm_z, 'sharp') node: TerrainNode = tile.node node.texture_set = progression_step.node_set decals: list[Decal] = [] for tile in node_tiles: node: TerrainNode = tile.node texture_set = node.texture_set assert texture_set in NODE_SET_PRIO for door in (1, 2, 3, 4): if door not in node.doors: continue floor_doors: dict[int, float] = FLOOR_DOORS[node.mesh_name] if door not in floor_doors: continue floor_door_height = floor_doors[door] neighbor_node, neighbor_door = node.doors[door] neighbor_texture_set = neighbor_node.texture_set assert neighbor_texture_set in NODE_SET_PRIO if NODE_SET_PRIO.index(texture_set) < NODE_SET_PRIO.index( neighbor_texture_set): decal_x, decal_z = MapgenTerrain.turn( 0, -2, math.tau / 4 * (door - 1)) if tile.node_mesh == 't_xxx_wal_12-thin': # wtf decal_x -= 7 decal_position = Position(decal_x, floor_door_height + 2, decal_z, node.guid) decal = Decal( guid=Hex.random(), texture=f'Art\\Bitmaps\\Decals\\b_d_{texture_set}-a.%img%', far_plane=2.2, decal_origin=decal_position) decals.append(decal) return decals
def from_gas(cls, decal_section: Section) -> Decal: assert decal_section.header == 't:decal,n:*' guid = decal_section.get_attr_value('guid') texture = decal_section.get_attr_value('texture') near_plane = decal_section.get_attr_value('near_plane') far_plane = decal_section.get_attr_value('far_plane') vertical_meters = decal_section.get_attr_value('vertical_meters') horizontal_meters = decal_section.get_attr_value('horizontal_meters') decal_origin = Position.parse( decal_section.get_attr_value('decal_origin')) decal_orientation = [ float(x) for x in decal_section.get_attr_value( 'decal_orientation').split(',') ] lod = decal_section.get_attr_value('lod') return Decal(guid, texture, near_plane, far_plane, vertical_meters, horizontal_meters, decal_origin, decal_orientation, lod)
def create_region(map_name, region_name, node='t_xxx_flr_04x04-v0'): bits = Bits() m = bits.maps[map_name] region: Region = m.create_region(region_name, None) region.terrain = Terrain(TerrainNode(None, node)) region.save() # start positions group if len(m.get_regions()) == 1 and m.start_positions is None: # 1st region, let's add a start pos position = Position(0, 0, 0, region.terrain.target_node.guid) m.start_positions = StartPositions( { 'default': StartGroup( 'This is the default group.', False, 0, '', [StartPos(1, position, Camera(0.5, 20, 0, position))]) }, 'default') m.save()
def generate_region(_map, region_name, size_x, size_z, args: Args, rt: RegionTiling): print(f'generate region {region_name} {size_x}x{size_z} ({args})') # generate the region! region_data = generate_region_data(size_x, size_z, args, region_name, rt, _map.get_all_node_ids()) if region_data is None: print('all tiles culled - not saving region') return terrain, plants, stitches, decals = region_data # add lighting ambient_color = Hex(0xff8080ff) ambient_intensity = 0.2 terrain.ambient_light.terrain_intensity = ambient_intensity terrain.ambient_light.terrain_color = ambient_color terrain.ambient_light.object_intensity = ambient_intensity terrain.ambient_light.object_color = ambient_color terrain.ambient_light.actor_intensity = ambient_intensity terrain.ambient_light.actor_color = ambient_color dir_lights = [ # daylight from south-east DirectionalLight( color=Color(0xffffffff), draw_shadow=True, intensity=1, occlude_geometry=True, on_timer=True, direction=DirectionalLight.direction_from_orbit_and_azimuth( 225, 45)), # counter-light from north-west DirectionalLight( color=Color(0xffffffff), draw_shadow=False, intensity=0.5, occlude_geometry=False, on_timer=True, direction=DirectionalLight.direction_from_orbit_and_azimuth( 45, 45)) ] # save if region_name in _map.get_regions(): print(f'deleting existing region {region_name}') _map.delete_region(region_name) _map.gas_dir.clear_cache() region_i = rt.cur_x * rt.num_z + rt.cur_z + 1 region: Region = _map.create_region(region_name, region_i) region.terrain = terrain region.decals = DecalsGas(decals) if len(decals) > 0 else None region.lights = dir_lights region.generated_objects_non_interactive = [] if args.start_pos is not None: pos = Position(0, 0, 0, terrain.target_node.guid) start_group_name = args.start_pos _map.load_start_positions() if start_group_name in _map.start_positions.start_groups: _map.start_positions.start_groups[ start_group_name].start_positions = [ StartPos(1, pos, Camera(0.5, 20, 0, pos)) ] else: sg_id = _map.start_positions.new_start_group_id() _map.start_positions.start_groups[start_group_name] = StartGroup( 'Heightmap generated start pos', False, sg_id, 'Heightmap', [StartPos(1, pos, Camera(0.5, 20, 0, pos))]) _map.start_positions.default = start_group_name region.generated_objects_non_interactive.append( GameObjectData( 'trigger_change_mood_box', placement=Placement(position=pos), common=Common(instance_triggers=[ TriggerInstance( 'party_member_within_bounding_box(2,1,2,"on_every_enter")', 'mood_change("map_world_df_r0_2")') ]))) _map.save() if plants is not None: region.generated_objects_non_interactive.extend([ GameObjectData( plant.template_name, placement=Placement(position=plant.position, orientation=Quaternion.rad_to_quat( plant.orientation)), aspect=Aspect(scale_multiplier=plant.size)) for plant in plants ]) if rt and (rt.num_x > 1 or rt.num_z > 1): top_stitches, left_stitches, bottom_stitches, right_stitches = stitches shg = StitchHelperGas(region.data.id, region_name) if top_stitches is not None: shg.stitch_editors.append( StitchEditor(rt.region_name(rt.cur_x, rt.cur_z - 1), top_stitches)) if left_stitches is not None: shg.stitch_editors.append( StitchEditor(rt.region_name(rt.cur_x - 1, rt.cur_z), left_stitches)) if bottom_stitches is not None: shg.stitch_editors.append( StitchEditor(rt.region_name(rt.cur_x, rt.cur_z + 1), bottom_stitches)) if right_stitches is not None: shg.stitch_editors.append( StitchEditor(rt.region_name(rt.cur_x + 1, rt.cur_z), right_stitches)) region.stitch_helper = shg region.save() print(f'new region {region_name} saved')
def generate_plants(terrain: Terrain, plants_profile: dict[str, float], include_nodes: list[NodeMask], exclude_nodes) -> list[Plant]: mesh_info = load_mesh_info() terrain_nodes = terrain.nodes if len(include_nodes) > 0: terrain_nodes = [ node for node in terrain_nodes if any([ node_mask.matches(node.section, node.level, node.object) for node_mask in include_nodes ]) ] if len(exclude_nodes) > 0: terrain_nodes = [ node for node in terrain_nodes if not any([ node_mask.matches(node.section, node.level, node.object) for node_mask in exclude_nodes ]) ] unknown_meshes = set([ node.mesh_name for node in terrain_nodes if node.mesh_name not in mesh_info ]) if len(unknown_meshes) > 0: print(str(len(unknown_meshes)) + ' unknown meshes:') for unknown_mesh in sorted(unknown_meshes): print(unknown_mesh) plantable_nodes = [ node for node in terrain_nodes if node.mesh_name in mesh_info and mesh_info[node.mesh_name] is not None ] print( f'nodes: {len(terrain.nodes)} total, {len(terrain_nodes)} included, {len(plantable_nodes)} plantable' ) overall_plantable_area_size = 0 for node in plantable_nodes: plantable_area_size = mesh_info[node.mesh_name].size() overall_plantable_area_size += plantable_area_size print(f'overall plantable area size: {overall_plantable_area_size} m²') plants = list() for template_name, density in plants_profile.items(): num_plants = int(overall_plantable_area_size * density) print(template_name + ' density ' + str(density) + '/m² -> num plants: ' + str(num_plants)) overall_weighted = 0 weighted_area_dist = list() for node in plantable_nodes: weight = random.uniform(0, 2) weighted = mesh_info[node.mesh_name].size() * weight overall_weighted += weighted weighted_area_dist.append((overall_weighted, node)) for i in range(num_plants): rand_val = random.uniform(0, overall_weighted) node = None for max_rand_val, n in weighted_area_dist: if max_rand_val > rand_val: node = n break plantable_area = mesh_info[node.mesh_name] x = random.uniform(plantable_area.x_min, plantable_area.x_max) z = random.uniform(plantable_area.z_min, plantable_area.z_max) y = plantable_area.y orientation = random.uniform(0, math.tau) size = random.uniform(0.8, 1.0) if random.choice( [True, False]) else random.uniform(1.0, 1.3) plant = Plant(template_name, Position(x, y, z, node.guid), orientation, size) plants.append(plant) return plants