extends Control ## A utility node for drawing 3D debug visuals (lines, rays, cubes) in screen space. @export var MouseOverlay: Node @onready var Cam = get_viewport().get_camera_3d() # Called when the node enters the scene tree for the first time. func _ready() -> void: if !OS.is_debug_build(): self.queue_free() return # Ensure the node processes and draws even when the game is paused. process_mode = Node.PROCESS_MODE_ALWAYS # Ensure the node is visible and set to draw in screen space. set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) ## A class representing a single debug line with start/end points, color, and duration. class Line: var Start:Vector3 var End:Vector3 var LineColor:Color var time:float ## Constructor for DebugLine. [br] ## [param _start] Starting point in 3D space.[br] ## [param _end] Ending point in 3D space.[br] ## [param _color] Color of the line.[br] ## [param _time] Duration the line persists (seconds). func _init(_Start:Vector3, _End:Vector3, _LineColor:Color, _time:float): self.Start = _Start self.End = _End self.LineColor = _LineColor self.time = _time ## Array storing all active debug lines. var Lines = [] ## Flag to track if a line was removed, triggering a redraw. var RemovedLine = false func _process(delta): for i in range(len(Lines)): Lines[i].time -= delta # Trigger a redraw if lines exist or a line was removed. if (len(Lines) > 0 || RemovedLine): queue_redraw() #Calls _draw RemovedLine = false func _draw(): # Exit early if no lines to draw or camera is not available. if Lines.is_empty() or not Cam: return for i in range(len(Lines)): if Lines[i].Start.length() == 0:return var ScreenPointStart = Cam.unproject_position(Lines[i].Start) var ScreenPointEnd = Cam.unproject_position(Lines[i].End) #Dont draw line if either start or end is considered behind the camera #this causes the line to not be drawn sometimes but avoids a bug where the #line is drawn incorrectly if (Cam.is_position_behind(Lines[i].Start) || Cam.is_position_behind(Lines[i].End)): continue draw_line(ScreenPointStart, ScreenPointEnd, Lines[i].LineColor) #Remove lines that have timed out var i = Lines.size() - 1 while (i >= 0): if (Lines[i].time < 0.0): Lines.remove_at(i) RemovedLine = true i -= 1 ## Draws a line between two 3D points. [br] ## [param start] Starting point in 3D space.[br] ## [param end] Ending point in 3D space.[br] ## [param time] Duration the line persists (seconds, default: 0.0 for one frame).[br] ## [param color] Color of the line (default: red, fully opaque). func DrawLine(Start, End, time := 0.0, LineColor := Color(1.0, 0.0, 0.0, 1.0)): Lines.append(Line.new(Start, End, LineColor, time)) ## Draws a ray from a starting point in a given direction.[br] ## [param start] Starting point in 3D space.[br] ## [param ray] Direction and magnitude of the ray (Vector3).[br] ## [param time] Duration the ray persists (seconds, default: 0.0 for one frame).[br] ## [param color] Color of the ray (default: red, fully opaque). func DrawRay(Start, Ray, time := 0.0, LineColor := Color(1.0, 0.0, 0.0, 1.0)): Lines.append(Line.new(Start, Start + Ray, LineColor, time)) ## Draws a wireframe cube in 3D space.[br] ## [param center] Center point of the cube in 3D space.[br] ## [param half_extents] Half the width of the cube (default: 0.1).[br] ## [param time] Duration the cube persists (seconds, default: 0.0 for one frame).[br] ## [param color] Color of the cube's edges (default: red, fully opaque). func DrawCube(Center, HalfExtents := 0.1, time := 0.0, LineColor := Color(1.0, 0.0, 0.0, 1.0)): #Start at the 'top left' var LinePointStart = Center LinePointStart.x -= HalfExtents LinePointStart.y += HalfExtents LinePointStart.z -= HalfExtents # Draw top square (four edges). var LinePointEnd = LinePointStart + Vector3(0, 0, HalfExtents * 2.0) DrawLine(LinePointStart, LinePointEnd, time, LineColor); LinePointStart = LinePointEnd LinePointEnd = LinePointStart + Vector3(HalfExtents * 2.0, 0, 0) DrawLine(LinePointStart, LinePointEnd, time, LineColor); LinePointStart = LinePointEnd LinePointEnd = LinePointStart + Vector3(0, 0, -HalfExtents * 2.0) DrawLine(LinePointStart, LinePointEnd, time, LineColor); LinePointStart = LinePointEnd LinePointEnd = LinePointStart + Vector3(-HalfExtents * 2.0, 0, 0) DrawLine(LinePointStart, LinePointEnd, time, LineColor); # Draw bottom square (four edges). LinePointStart = LinePointEnd + Vector3(0, -HalfExtents * 2.0, 0) LinePointEnd = LinePointStart + Vector3(0, 0, HalfExtents * 2.0) DrawLine(LinePointStart, LinePointEnd, time, LineColor); LinePointStart = LinePointEnd LinePointEnd = LinePointStart + Vector3(HalfExtents * 2.0, 0, 0) DrawLine(LinePointStart, LinePointEnd, time, LineColor); LinePointStart = LinePointEnd LinePointEnd = LinePointStart + Vector3(0, 0, -HalfExtents * 2.0) DrawLine(LinePointStart, LinePointEnd, time, LineColor); LinePointStart = LinePointEnd LinePointEnd = LinePointStart + Vector3(-HalfExtents * 2.0, 0, 0) DrawLine(LinePointStart, LinePointEnd, time, LineColor); # Draw vertical lines (four edges connecting top and bottom squares). LinePointStart = LinePointEnd DrawRay(LinePointStart, Vector3(0, HalfExtents * 2.0, 0),time, LineColor) LinePointStart += Vector3(0, 0, HalfExtents * 2.0) DrawRay(LinePointStart, Vector3(0, HalfExtents * 2.0, 0), time, LineColor) LinePointStart += Vector3(HalfExtents * 2.0, 0, 0) DrawRay(LinePointStart, Vector3(0, HalfExtents * 2.0, 0), time, LineColor) LinePointStart += Vector3(0, 0, -HalfExtents * 2.0) DrawRay(LinePointStart, Vector3(0, HalfExtents * 2.0, 0), time, LineColor) func DrawSphere(Position:Vector3): var SphereShape = CSGSphere3D.new() var node = Node3D.new() node.add_child(SphereShape) node.position = Position get_tree().root.add_child(node)