def save_embeddedvoxel(item: Item, vmf: VMF) -> None: """Save embedded voxel volumes.""" for bbox_min, bbox_max in bounding_boxes(item.embed_voxels): vmf.create_ent('bee2_editor_embeddedvoxel').solids.append( vmf.make_prism( Vec(bbox_min) * 128 + (-64.0, -64.0, -192.0), Vec(bbox_max) * 128 + (+64.0, +64.0, -64.0), # Entirely ignored, but makes it easier to distinguish. 'tools/toolshint', ).solid)
def save_occupiedvoxel(item: Item, vmf: VMF) -> None: """Save occupied voxel volumes.""" for voxel in item.occupy_voxels: pos = Vec(voxel.pos) * 128 if voxel.subpos is not None: pos += Vec(voxel.subpos) * 32 - (48, 48, 48) p1 = pos - (16.0, 16.0, 16.0) p2 = pos + (16.0, 16.0, 16.0) norm_dist = 32.0 - 4.0 else: p1 = pos - (64.0, 64.0, 64.0) p2 = pos + (64.0, 64.0, 64.0) norm_dist = 128.0 - 4.0 if voxel.normal is not None: for axis in ['x', 'y', 'z']: val = getattr(voxel.normal, axis) if val == +1: p2[axis] -= norm_dist elif val == -1: p1[axis] += norm_dist if voxel.against is not None: against = str(voxel.against).replace('COLLIDE_', '') else: against = '' vmf.create_ent( 'bee2_editor_occupiedvoxel', coll_type=str(voxel.type).replace('COLLIDE_', ''), coll_against=against, ).solids.append( vmf.make_prism( p1, p2, # Use clip for voxels, invisible for normals. # Entirely ignored, but makes it easier to use. 'tools/toolsclip' if voxel.normal is None else 'tools/toolsinvisible', ).solid)
def setup(self, vmf: VMF, global_seed: str, tiles: List['TileDef']) -> None: """Build the list of clump locations.""" assert self.portal is not None assert self.orient is not None # Convert the generator key to a generator-specific seed. # That ensures different surfaces don't end up reusing the same # texture indexes. self.gen_seed = int.from_bytes( self.category.name.encode() + self.portal.name.encode() + self.orient.name.encode(), 'big', ) LOGGER.info('Generating texture clumps...') clump_length: int = self.options['clump_length'] clump_width: int = self.options['clump_width'] # The tiles currently present in the map. orient_z = self.orient.z remaining_tiles: Set[Tuple[float, float, float]] = { (tile.pos + 64 * tile.normal // 128 * 128).as_tuple() for tile in tiles if tile.normal.z == orient_z } # A global RNG for picking clump positions. clump_rand = random.Random(global_seed + '_clumping') pos_min = Vec() pos_max = Vec() # For debugging, generate skip brushes with the shape of the clumps. debug_visgroup: Optional[VisGroup] if self.options['clump_debug']: debug_visgroup = vmf.create_visgroup( f'{self.category.name}_{self.orient.name}_{self.portal.name}') else: debug_visgroup = None while remaining_tiles: # Pick from a random tile. tile_pos = next( itertools.islice( remaining_tiles, clump_rand.randrange(0, len(remaining_tiles)), len(remaining_tiles), )) remaining_tiles.remove(tile_pos) pos = Vec(tile_pos) # Clumps are long strips mainly extended in one direction # In the other directions extend by 'width'. It can point any axis. direction = clump_rand.choice('xyz') for axis in 'xyz': if axis == direction: dist = clump_length else: dist = clump_width pos_min[axis] = pos[axis] - clump_rand.randint(0, dist) * 128 pos_max[axis] = pos[axis] + clump_rand.randint(0, dist) * 128 remaining_tiles.difference_update( map(Vec.as_tuple, Vec.iter_grid(pos_min, pos_max, 128))) self._clump_locs.append( Clump( pos_min.x, pos_min.y, pos_min.z, pos_max.x, pos_max.y, pos_max.z, # We use this to reseed an RNG, giving us the same textures # each time for the same clump. clump_rand.getrandbits(32), )) if debug_visgroup is not None: # noinspection PyUnboundLocalVariable debug_brush: Solid = vmf.make_prism( pos_min - 64, pos_max + 64, 'tools/toolsskip', ).solid debug_brush.visgroup_ids.add(debug_visgroup.id) debug_brush.vis_shown = False vmf.add_brush(debug_brush) LOGGER.info( '{}.{}.{}: {} Clumps for {} tiles', self.category.name, self.orient.name, self.portal.name, len(self._clump_locs), len(tiles), )