Module: Quake::Bsp::Vis
- Defined in:
- lib/quake/bsp/vis.rb
Overview
PVS (Potentially Visible Set) decompression and leaf lookup. Each leaf stores a compressed bitvector indicating which other leaves are visible from it. The compression is simple RLE: a zero byte is followed by a count of zero bytes to insert.
Class Method Summary collapse
- .all_visible(num_leafs) ⇒ Object
-
.decompress_pvs(visibility, vis_offset, num_leafs) ⇒ Object
Decompress the PVS for a leaf into an array of visible leaf indices.
-
.point_in_leaf(level, point) ⇒ Object
Find which leaf a point is in by walking the BSP node tree.
-
.visible_faces(level, leaf_index) ⇒ Object
Mark which faces are visible from the given leaf.
Class Method Details
.all_visible(num_leafs) ⇒ Object
106 107 108 |
# File 'lib/quake/bsp/vis.rb', line 106 def self.all_visible(num_leafs) Set.new(1..num_leafs) end |
.decompress_pvs(visibility, vis_offset, num_leafs) ⇒ Object
Decompress the PVS for a leaf into an array of visible leaf indices. visibility - raw visibility lump data (String) vis_offset - byte offset into visibility data for this leaf num_leafs - total number of leafs in the level (excluding leaf 0) Returns a Set of visible leaf indices (1-based, matching leafs array).
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/quake/bsp/vis.rb', line 15 def self.decompress_pvs(visibility, vis_offset, num_leafs) return all_visible(num_leafs) if vis_offset < 0 || visibility.nil? || visibility.empty? num_bytes = (num_leafs + 7) / 8 pvs = String.new("\0" * num_bytes, encoding: Encoding::BINARY) src = vis_offset dst = 0 while dst < num_bytes byte = visibility.getbyte(src) # Vis data ended before filling the bitvector. Remaining bytes # are already zero (no additional visible leaves), which is safe. break if byte.nil? src += 1 if byte != 0 pvs.setbyte(dst, byte) dst += 1 else # RLE: next byte is count of zero bytes count = visibility.getbyte(src) || 0 src += 1 dst += count # pvs already zeroed end end # Convert bitvector to set of leaf indices visible = Set.new num_leafs.times do |i| byte_idx = i / 8 bit_idx = i % 8 if pvs.getbyte(byte_idx) & (1 << bit_idx) != 0 visible << (i + 1) # leaf indices are 1-based (leaf 0 is the solid leaf) end end visible end |
.point_in_leaf(level, point) ⇒ Object
Find which leaf a point is in by walking the BSP node tree.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/quake/bsp/vis.rb', line 55 def self.point_in_leaf(level, point) node_index = 0 loop do node = level.nodes[node_index] plane = level.planes[node.plane_index] dist = point.dot(plane.normal) - plane.dist child = dist >= 0 ? node.children[0] : node.children[1] # Negative child index means leaf: ~child gives leaf index if child < 0 return ~child else node_index = child end end end |
.visible_faces(level, leaf_index) ⇒ Object
Mark which faces are visible from the given leaf. Returns a Set of face indices that should be rendered.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/quake/bsp/vis.rb', line 76 def self.visible_faces(level, leaf_index) leaf = level.leafs[leaf_index] return Set.new if leaf.nil? num_leafs = level.leafs.size - 1 # exclude leaf 0 # Leaf 0 is the universal SOLID leaf and has no real PVS; its # vis_offset is unused but Quake BSPs sometimes store 0 here, which # would otherwise decompress garbage. Match TyrQuake Mod_LeafPVS: # treat the solid leaf as all-visible so a camera that ends up in # solid space (noclip, edge cases) still sees the world. visible_leafs = if leaf_index == 0 all_visible(num_leafs) else decompress_pvs(level.visibility, leaf.vis_offset, num_leafs) end face_set = Set.new visible_leafs.each do |li| vl = level.leafs[li] next if vl.nil? vl.num_marksurfaces.times do |i| face_idx = level.marksurfaces[vl.first_marksurface + i] face_set << face_idx end end face_set end |