def make_barriers(vmf: VMF): """Make barrier entities. get_tex is vbsp.get_tex.""" glass_temp = template_brush.get_scaling_template( options.get(str, "glass_template")) grate_temp = template_brush.get_scaling_template( options.get(str, "grating_template")) hole_temp_small: List[Solid] hole_temp_lrg_diag: List[Solid] hole_temp_lrg_cutout: List[Solid] hole_temp_lrg_square: List[Solid] # Avoid error without this package. if HOLES: # Grab the template solids we need. hole_combined_temp = template_brush.get_template( options.get(str, 'glass_hole_temp')) hole_world, hole_detail, _ = hole_combined_temp.visgrouped({'small'}) hole_temp_small = hole_world + hole_detail hole_world, hole_detail, _ = hole_combined_temp.visgrouped( {'large_diagonal'}) hole_temp_lrg_diag = hole_world + hole_detail hole_world, hole_detail, _ = hole_combined_temp.visgrouped( {'large_cutout'}) hole_temp_lrg_cutout = hole_world + hole_detail hole_world, hole_detail, _ = hole_combined_temp.visgrouped( {'large_square'}) hole_temp_lrg_square = hole_world + hole_detail else: hole_temp_small = hole_temp_lrg_diag = hole_temp_lrg_cutout = hole_temp_lrg_square = [] floorbeam_temp = options.get(str, 'glass_floorbeam_temp') if options.get_itemconf('BEE_PELLET:PelletGrating', False): # Merge together these existing filters in global_pti_ents vmf.create_ent( origin=options.get(Vec, 'global_pti_ents_loc'), targetname='@grating_filter', classname='filter_multi', filtertype=0, negated=0, filter01='@not_pellet', filter02='@not_paint_bomb', ) else: # Just skip paint bombs. vmf.create_ent( origin=options.get(Vec, 'global_pti_ents_loc'), targetname='@grating_filter', classname='filter_activator_class', negated=1, filterclass='prop_paint_bomb', ) # Group the positions by planes in each orientation. # This makes them 2D grids which we can optimise. # (normal_dist, positive_axis, type) -> [(x, y)] slices: Dict[Tuple[Tuple[float, float, float], bool, BarrierType], Dict[Tuple[int, int], False]] = defaultdict(dict) # We have this on the 32-grid so we can cut squares for holes. for (origin_tup, normal_tup), barr_type in BARRIERS.items(): origin = Vec(origin_tup) normal = Vec(normal_tup) norm_axis = normal.axis() u, v = origin.other_axes(norm_axis) norm_pos = Vec.with_axes(norm_axis, origin) slice_plane = slices[ norm_pos.as_tuple(), # distance from origin to this plane. normal[norm_axis] > 0, barr_type, ] for u_off in [-48, -16, 16, 48]: for v_off in [-48, -16, 16, 48]: slice_plane[int((u + u_off) // 32), int((v + v_off) // 32), ] = True # Remove pane sections where the holes are. We then generate those with # templates for slanted parts. for (origin_tup, norm_tup), hole_type in HOLES.items(): barr_type = BARRIERS[origin_tup, norm_tup] origin = Vec(origin_tup) normal = Vec(norm_tup) norm_axis = normal.axis() u, v = origin.other_axes(norm_axis) norm_pos = Vec.with_axes(norm_axis, origin) slice_plane = slices[norm_pos.as_tuple(), normal[norm_axis] > 0, barr_type, ] if hole_type is HoleType.LARGE: offsets = (-80, -48, -16, 16, 48, 80) else: offsets = (-16, 16) for u_off in offsets: for v_off in offsets: # Remove these squares, but keep them in the dict # so we can check if there was glass there. uv = ( int((u + u_off) // 32), int((v + v_off) // 32), ) if uv in slice_plane: slice_plane[uv] = False # These have to be present, except for the corners # on the large hole. elif abs(u_off) != 80 or abs(v_off) != 80: u_ax, v_ax = Vec.INV_AXIS[norm_axis] LOGGER.warning( 'Hole tried to remove missing tile at ({})?', Vec.with_axes(norm_axis, norm_pos, u_ax, u + u_off, v_ax, v + v_off), ) # Now generate the curved brushwork. if barr_type is BarrierType.GLASS: front_temp = glass_temp elif barr_type is BarrierType.GRATING: front_temp = grate_temp else: raise NotImplementedError angles = normal.to_angle() hole_temp: List[Tuple[List[Solid], Matrix]] = [] # This is a tricky bit. Two large templates would collide # diagonally, and we allow the corner glass to not be present since # the hole doesn't actually use that 32x32 segment. # So we need to determine which of 3 templates to use. corn_angles = angles.copy() if hole_type is HoleType.LARGE: for corn_angles.roll in (0, 90, 180, 270): corn_mat = Matrix.from_angle(corn_angles) corn_dir = Vec(y=1, z=1) @ corn_angles hole_off = origin + 128 * corn_dir diag_type = HOLES.get( (hole_off.as_tuple(), normal.as_tuple()), None, ) corner_pos = origin + 80 * corn_dir corn_u, corn_v = corner_pos.other_axes(norm_axis) corn_u = int(corn_u // 32) corn_v = int(corn_v // 32) if diag_type is HoleType.LARGE: # There's another large template to this direction. # Just have 1 generate both combined, so the brushes can # be more optimal. To pick, arbitrarily make the upper one # be in charge. if corn_v > v // 32: hole_temp.append((hole_temp_lrg_diag, corn_mat)) continue if (corn_u, corn_v) in slice_plane: hole_temp.append((hole_temp_lrg_square, corn_mat)) else: hole_temp.append((hole_temp_lrg_cutout, corn_mat)) else: hole_temp.append((hole_temp_small, Matrix.from_angle(angles))) def solid_pane_func(off1: float, off2: float, mat: str) -> List[Solid]: """Given the two thicknesses, produce the curved hole from the template.""" off_min = 64 - max(off1, off2) off_max = 64 - min(off1, off2) new_brushes = [] for brushes, matrix in hole_temp: for orig_brush in brushes: brush = orig_brush.copy(vmf_file=vmf) new_brushes.append(brush) for face in brush.sides: face.mat = mat for point in face.planes: if point.x > 64: point.x = off_max else: point.x = off_min face.localise(origin, matrix) # Increase precision, these are small detail brushes. face.lightmap = 8 return new_brushes make_glass_grating( vmf, origin, normal, barr_type, front_temp, solid_pane_func, ) for (plane_pos, is_pos, barr_type), pos_slice in slices.items(): plane_pos = Vec(plane_pos) norm_axis = plane_pos.axis() normal = Vec.with_axes(norm_axis, 1 if is_pos else -1) if barr_type is BarrierType.GLASS: front_temp = glass_temp elif barr_type is BarrierType.GRATING: front_temp = grate_temp else: raise NotImplementedError u_axis, v_axis = Vec.INV_AXIS[norm_axis] for min_u, min_v, max_u, max_v in grid_optimise(pos_slice): # These are two points in the origin plane, at the borders. pos_min = Vec.with_axes( norm_axis, plane_pos, u_axis, min_u * 32, v_axis, min_v * 32, ) pos_max = Vec.with_axes( norm_axis, plane_pos, u_axis, max_u * 32 + 32, v_axis, max_v * 32 + 32, ) def solid_pane_func(pos1: float, pos2: float, mat: str) -> List[Solid]: """Make the solid brush.""" return [ vmf.make_prism( pos_min + normal * (64.0 - pos1), pos_max + normal * (64.0 - pos2), mat=mat, ).solid ] make_glass_grating( vmf, (pos_min + pos_max) / 2 + 63 * normal, normal, barr_type, front_temp, solid_pane_func, ) # Generate hint brushes, to ensure sorting is done correctly. [hint] = solid_pane_func(0, 4.0, consts.Tools.SKIP) for side in hint: if abs(Vec.dot(side.normal(), normal)) > 0.99: side.mat = consts.Tools.HINT vmf.add_brush(hint) if floorbeam_temp: LOGGER.info('Adding Glass floor beams...') add_glass_floorbeams(vmf, floorbeam_temp) LOGGER.info('Done!')
def make_barriers(vmf: VMF, get_tex: Callable[[str], str]): """Make barrier entities. get_tex is vbsp.get_tex.""" glass_temp = template_brush.get_scaling_template( vbsp_options.get(str, "glass_template") ) grate_temp = template_brush.get_scaling_template( vbsp_options.get(str, "grating_template") ) # Avoid error without this package. if HOLES: # Grab the template solids we need. hole_temp = template_brush.get_template( vbsp_options.get(str, 'glass_hole_temp') ) hole_world, hole_detail, _ = hole_temp.visgrouped({'small'}) hole_temp_small = hole_world + hole_detail hole_world, hole_detail, _ = hole_temp.visgrouped({'large'}) hole_temp_large = hole_world + hole_detail hole_world, hole_detail, _ = hole_temp.visgrouped({'large_corner'}) hole_temp_corner = hole_world + hole_detail else: hole_temp_small = hole_temp_large = hole_temp_corner = None floorbeam_temp = vbsp_options.get(str, 'glass_floorbeam_temp') if vbsp_options.get_itemconf('BEE_PELLET:PelletGrating', False): # Merge together these existing filters in global_pti_ents vmf.create_ent( origin=vbsp_options.get(Vec, 'global_pti_ents_loc'), targetname='@grating_filter', classname='filter_multi', filtertype=0, negated=0, filter01='@not_pellet', filter02='@not_paint_bomb', ) else: # Just skip paint bombs. vmf.create_ent( origin=vbsp_options.get(Vec, 'global_pti_ents_loc'), targetname='@grating_filter', classname='filter_activator_class', negated=1, filterclass='prop_paint_bomb', ) # Group the positions by planes in each orientation. # This makes them 2D grids which we can optimise. # (normal_dist, positive_axis, type) -> [(x, y)] slices = defaultdict(set) # type: Dict[Tuple[Tuple[float, float, float], bool, BarrierType], Set[Tuple[float, float]]] # We have this on the 32-grid so we can cut squares for holes. for (origin, normal), barr_type in BARRIERS.items(): origin = Vec(origin) normal = Vec(normal) norm_axis = normal.axis() u, v = origin.other_axes(norm_axis) norm_pos = Vec.with_axes(norm_axis, origin) slice_plane = slices[ norm_pos.as_tuple(), # distance from origin to this plane. normal[norm_axis] > 0, barr_type, ] for u_off in [-48, -16, 16, 48]: for v_off in [-48, -16, 16, 48]: slice_plane.add(( (u + u_off) // 32, (v + v_off) // 32, )) # Remove pane sections where the holes are. We then generate those with # templates for slanted parts. for (origin, normal), hole_type in HOLES.items(): barr_type = BARRIERS[origin, normal] origin = Vec(origin) normal = Vec(normal) norm_axis = normal.axis() u, v = origin.other_axes(norm_axis) norm_pos = Vec.with_axes(norm_axis, origin) slice_plane = slices[ norm_pos.as_tuple(), normal[norm_axis] > 0, barr_type, ] if hole_type is HoleType.LARGE: offsets = (-80, -48, -16, 16, 48, 80) hole_temp = hole_temp_large.copy() else: offsets = (-16, 16) hole_temp = hole_temp_small.copy() for u_off in offsets: for v_off in offsets: # Skip the corners on large holes. # Those aren't actually used, so skip them. That way # we can have them diagonally or without glass in the corner. if u_off in (-80, 80) and v_off in (-80, 80): continue slice_plane.discard(( (u + u_off) // 32, (v + v_off) // 32, )) # Now generate the curved brushwork. if barr_type is BarrierType.GLASS: front_temp = glass_temp front_mat = get_tex('special.glass') elif barr_type is BarrierType.GRATING: front_temp = grate_temp front_mat = get_tex('special.grating') else: raise NotImplementedError angles = normal.to_angle(0) # Angle corresponding to the brush, for the corners. angle_list = [angles] * len(hole_temp) # This is a tricky bit. Two large templates would collide # diagonally, # so chop off the corners, then put them back only if there's not # one diagonally. if hole_type is HoleType.LARGE: for roll in (0, 90, 180, 270): corn_angles = angles.copy() corn_angles.z = roll hole_off = origin + Vec(y=128, z=128).rotate(*corn_angles) diag_type = HOLES.get( (hole_off.as_tuple(), normal.as_tuple()), None, ) if diag_type is not HoleType.LARGE: hole_temp += hole_temp_corner angle_list += [corn_angles] * len(hole_temp_corner) def solid_pane_func(off1, off2, mat): """Given the two thicknesses, produce the curved hole from the template.""" off_min = min(off1, off2) off_max = max(off1, off2) new_brushes = [ brush.copy(vmf_file=vmf) for brush in hole_temp ] for brush, br_angles in zip(new_brushes, angle_list): for face in brush.sides: face.mat = mat f_norm = face.normal() if f_norm.x == 1: face.translate(Vec(x=4 - off_max)) # face.mat = 'min' elif f_norm.x == -1: face.translate(Vec(x=-4 - off_min)) # face.mat = 'max' face.localise(origin, br_angles) return new_brushes make_glass_grating( vmf, origin, normal, barr_type, front_temp, front_mat, solid_pane_func, ) for (plane_pos, is_pos, barr_type), pos_slice in slices.items(): plane_pos = Vec(plane_pos) norm_axis = plane_pos.axis() normal = Vec.with_axes(norm_axis, 1 if is_pos else -1) if barr_type is BarrierType.GLASS: front_temp = glass_temp front_mat = get_tex('special.glass') elif barr_type is BarrierType.GRATING: front_temp = grate_temp front_mat = get_tex('special.grating') else: raise NotImplementedError u_axis, v_axis = Vec.INV_AXIS[norm_axis] for min_u, min_v, max_u, max_v in grid_optimise(dict.fromkeys(pos_slice, True)): # These are two points in the origin plane, at the borders. pos_min = Vec.with_axes( norm_axis, plane_pos, u_axis, min_u * 32, v_axis, min_v * 32, ) pos_max = Vec.with_axes( norm_axis, plane_pos, u_axis, max_u * 32 + 32, v_axis, max_v * 32 + 32, ) def solid_pane_func(pos1, pos2, mat): """Make the solid brush.""" return [vmf.make_prism( pos_min + normal * (64.0 - pos1), pos_max + normal * (64.0 - pos2), mat=mat, ).solid] make_glass_grating( vmf, (pos_min + pos_max)/2, normal, barr_type, front_temp, front_mat, solid_pane_func, ) if floorbeam_temp: LOGGER.info('Adding Glass floor beams...') add_glass_floorbeams(vmf, floorbeam_temp) LOGGER.info('Done!')
def make_barriers(vmf: VMF): """Make barrier entities. get_tex is vbsp.get_tex.""" glass_temp = template_brush.get_scaling_template( vbsp_options.get(str, "glass_template")) grate_temp = template_brush.get_scaling_template( vbsp_options.get(str, "grating_template")) # Avoid error without this package. if HOLES: # Grab the template solids we need. hole_temp = template_brush.get_template( vbsp_options.get(str, 'glass_hole_temp')) hole_world, hole_detail, _ = hole_temp.visgrouped({'small'}) hole_temp_small = hole_world + hole_detail hole_world, hole_detail, _ = hole_temp.visgrouped({'large'}) hole_temp_large = hole_world + hole_detail hole_world, hole_detail, _ = hole_temp.visgrouped({'large_corner'}) hole_temp_corner = hole_world + hole_detail else: hole_temp_small = hole_temp_large = hole_temp_corner = None floorbeam_temp = vbsp_options.get(str, 'glass_floorbeam_temp') if vbsp_options.get_itemconf('BEE_PELLET:PelletGrating', False): # Merge together these existing filters in global_pti_ents vmf.create_ent( origin=vbsp_options.get(Vec, 'global_pti_ents_loc'), targetname='@grating_filter', classname='filter_multi', filtertype=0, negated=0, filter01='@not_pellet', filter02='@not_paint_bomb', ) else: # Just skip paint bombs. vmf.create_ent( origin=vbsp_options.get(Vec, 'global_pti_ents_loc'), targetname='@grating_filter', classname='filter_activator_class', negated=1, filterclass='prop_paint_bomb', ) # Group the positions by planes in each orientation. # This makes them 2D grids which we can optimise. # (normal_dist, positive_axis, type) -> [(x, y)] slices = defaultdict( set ) # type: Dict[Tuple[Tuple[float, float, float], bool, BarrierType], Set[Tuple[float, float]]] # We have this on the 32-grid so we can cut squares for holes. for (origin, normal), barr_type in BARRIERS.items(): origin = Vec(origin) normal = Vec(normal) norm_axis = normal.axis() u, v = origin.other_axes(norm_axis) norm_pos = Vec.with_axes(norm_axis, origin) slice_plane = slices[ norm_pos.as_tuple(), # distance from origin to this plane. normal[norm_axis] > 0, barr_type, ] for u_off in [-48, -16, 16, 48]: for v_off in [-48, -16, 16, 48]: slice_plane.add(( (u + u_off) // 32, (v + v_off) // 32, )) # Remove pane sections where the holes are. We then generate those with # templates for slanted parts. for (origin, normal), hole_type in HOLES.items(): barr_type = BARRIERS[origin, normal] origin = Vec(origin) normal = Vec(normal) norm_axis = normal.axis() u, v = origin.other_axes(norm_axis) norm_pos = Vec.with_axes(norm_axis, origin) slice_plane = slices[norm_pos.as_tuple(), normal[norm_axis] > 0, barr_type, ] if hole_type is HoleType.LARGE: offsets = (-80, -48, -16, 16, 48, 80) hole_temp = hole_temp_large.copy() else: offsets = (-16, 16) hole_temp = hole_temp_small.copy() for u_off in offsets: for v_off in offsets: # Skip the corners on large holes. # Those aren't actually used, so skip them. That way # we can have them diagonally or without glass in the corner. if u_off in (-80, 80) and v_off in (-80, 80): continue slice_plane.discard(( (u + u_off) // 32, (v + v_off) // 32, )) # Now generate the curved brushwork. if barr_type is BarrierType.GLASS: front_temp = glass_temp elif barr_type is BarrierType.GRATING: front_temp = grate_temp else: raise NotImplementedError angles = normal.to_angle(0) # Angle corresponding to the brush, for the corners. angle_list = [angles] * len(hole_temp) # This is a tricky bit. Two large templates would collide # diagonally, # so chop off the corners, then put them back only if there's not # one diagonally. if hole_type is HoleType.LARGE: for roll in (0, 90, 180, 270): corn_angles = angles.copy() corn_angles.z = roll hole_off = origin + Vec(y=128, z=128).rotate(*corn_angles) diag_type = HOLES.get( (hole_off.as_tuple(), normal.as_tuple()), None, ) if diag_type is not HoleType.LARGE: hole_temp += hole_temp_corner angle_list += [corn_angles] * len(hole_temp_corner) def solid_pane_func(off1, off2, mat): """Given the two thicknesses, produce the curved hole from the template.""" off_min = min(off1, off2) off_max = max(off1, off2) new_brushes = [brush.copy(vmf_file=vmf) for brush in hole_temp] for brush, br_angles in zip(new_brushes, angle_list): for face in brush.sides: face.mat = mat f_norm = face.normal() if f_norm.x == 1: face.translate(Vec(x=4 - off_max)) # face.mat = 'min' elif f_norm.x == -1: face.translate(Vec(x=-4 - off_min)) # face.mat = 'max' face.localise(origin, br_angles) return new_brushes make_glass_grating( vmf, origin, normal, barr_type, front_temp, solid_pane_func, ) for (plane_pos, is_pos, barr_type), pos_slice in slices.items(): plane_pos = Vec(plane_pos) norm_axis = plane_pos.axis() normal = Vec.with_axes(norm_axis, 1 if is_pos else -1) if barr_type is BarrierType.GLASS: front_temp = glass_temp elif barr_type is BarrierType.GRATING: front_temp = grate_temp else: raise NotImplementedError u_axis, v_axis = Vec.INV_AXIS[norm_axis] for min_u, min_v, max_u, max_v in grid_optimise( dict.fromkeys(pos_slice, True)): # These are two points in the origin plane, at the borders. pos_min = Vec.with_axes( norm_axis, plane_pos, u_axis, min_u * 32, v_axis, min_v * 32, ) pos_max = Vec.with_axes( norm_axis, plane_pos, u_axis, max_u * 32 + 32, v_axis, max_v * 32 + 32, ) def solid_pane_func(pos1, pos2, mat): """Make the solid brush.""" return [ vmf.make_prism( pos_min + normal * (64.0 - pos1), pos_max + normal * (64.0 - pos2), mat=mat, ).solid ] make_glass_grating( vmf, (pos_min + pos_max) / 2, normal, barr_type, front_temp, solid_pane_func, ) if floorbeam_temp: LOGGER.info('Adding Glass floor beams...') add_glass_floorbeams(vmf, floorbeam_temp) LOGGER.info('Done!')