Module: Quake::Physics::HullTrace
- Defined in:
- lib/quake/physics/hull_trace.rb
Class Method Summary collapse
-
.point_contents(clipnodes, planes, node_index, point) ⇒ Object
Check what content type a point is in, using the clipnode tree.
-
.trace(clipnodes, planes, node_index, p1, p2) ⇒ Object
Trace a line from p1 to p2 through the hull.
-
.trace_world_and_entities(level, p1, p2, brush_entities, hull_num: 1) ⇒ Object
Trace against the world AND all solid brush entities, returning the nearest hit.
Class Method Details
.point_contents(clipnodes, planes, node_index, point) ⇒ Object
Check what content type a point is in, using the clipnode tree. hull_clipnodes: array of ClipNode planes: array of Plane node_index: starting clipnode index point: Vec3
31 32 33 34 35 36 37 38 39 40 |
# File 'lib/quake/physics/hull_trace.rb', line 31 def self.point_contents(clipnodes, planes, node_index, point) while node_index >= 0 node = clipnodes[node_index] plane = planes[node.plane_index] dist = point.dot(plane.normal) - plane.dist node_index = dist >= 0 ? node.children[0] : node.children[1] end node_index # negative value = content type end |
.trace(clipnodes, planes, node_index, p1, p2) ⇒ Object
Trace a line from p1 to p2 through the hull. Returns a TraceResult.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/quake/physics/hull_trace.rb', line 44 def self.trace(clipnodes, planes, node_index, p1, p2) result = { all_solid: false, start_solid: false, in_open: false, in_water: false, fraction: 1.0, end_pos: p2, plane_normal: nil, plane_dist: nil } recursive_trace(clipnodes, planes, node_index, 0.0, 1.0, p1, p2, result) if result[:fraction] == 1.0 result[:end_pos] = p2 else result[:end_pos] = Math::Vec3.new( p1.x + result[:fraction] * (p2.x - p1.x), p1.y + result[:fraction] * (p2.y - p1.y), p1.z + result[:fraction] * (p2.z - p1.z) ) end TraceResult.new(**result) end |
.trace_world_and_entities(level, p1, p2, brush_entities, hull_num: 1) ⇒ Object
Trace against the world AND all solid brush entities, returning the nearest hit. Brush entity traces are done in entity-local space (subtract origin, trace sub-model hull, transform back).
70 71 72 73 74 75 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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/quake/physics/hull_trace.rb', line 70 def self.trace_world_and_entities(level, p1, p2, brush_entities, hull_num: 1) clipnodes = level.clipnodes planes = level.planes # World trace world_hull = level.models[0].head_nodes[hull_num] world_hull = level.models[0].head_nodes[0] if world_hull < 0 || world_hull >= clipnodes.size best = trace(clipnodes, planes, world_hull, p1, p2) # Trace against each brush entity's sub-model brush_entities&.each do |ent| next unless ent.brush_entity? # Triggers are SOLID_TRIGGER in Quake: they detect overlap but # don't block movement. They're handled by check_triggers. next if ent.classname.start_with?("trigger_") model = level.models[ent.model_index] next unless model sub_hull = model.head_nodes[hull_num] # Many sub-models only have hull 0; fall back gracefully sub_hull = model.head_nodes[0] if sub_hull < 0 || sub_hull >= clipnodes.size next if sub_hull < 0 || sub_hull >= clipnodes.size # Transform trace into entity-local space offset = ent.position local_p1 = Math::Vec3.new(p1.x - offset.x, p1.y - offset.y, p1.z - offset.z) local_p2 = Math::Vec3.new(p2.x - offset.x, p2.y - offset.y, p2.z - offset.z) sub_result = trace(clipnodes, planes, sub_hull, local_p1, local_p2) # Keep nearest hit if sub_result.fraction < best.fraction # Transform end_pos back to world space ep = sub_result.end_pos best = TraceResult.new( all_solid: sub_result.all_solid, start_solid: sub_result.start_solid, in_open: sub_result.in_open, in_water: sub_result.in_water, fraction: sub_result.fraction, end_pos: Math::Vec3.new(ep.x + offset.x, ep.y + offset.y, ep.z + offset.z), plane_normal: sub_result.plane_normal, plane_dist: sub_result.plane_dist ) end end best end |