├── LICENSE ├── README.md └── debug_geometry.gd /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Cyril K.B. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GodotDebugGeometry 2 | Godot GDScript used to draw geometry for development and debugging purposes. 3 | 4 | A demo project displaying random shapes [is available here](https://github.com/Cykyrios/GodotDebugGeometry-demo). 5 | 6 | ## Features 7 | Enables easily drawing objects for debugging purposes. 8 | 9 | The list of currently supported shapes is the following: 10 | * Cube (axis-oriented box) 11 | * Sphere 12 | * Cylinder 13 | * Cone 14 | * Arrow 15 | * Coordinate system (set of 3 colored arrows) 16 | * Grid 17 | * Line 18 | * Point 19 | 20 | Most available shapes can be drawn as either lines or solid geometry and in arbitrary orientations. Debug Lines can have non-zero thickness and are then drawn as a solid cylinder. All functions have a time argument which determines how long a shape will stay in the world before being erased. A time value of 0 will display the shape for a single frame. 21 | 22 | ## Usage 23 | Make debug_geometry.gd an autoload node. To add a shape to the world, you can call `DebugGeometry.draw_debug_shape` where `shape` is any of the available shapes. 24 | 25 | Keep in mind that all shapes are drawn relative to the DebugGeometry node, which is at the top level of the scene tree, being an autoload. 26 | 27 | If you do not want to use it as an autoload, I recommend adding `class_name DebugGeometry` at the top of the file, and placing a `MeshInstance3D` with this script attached where you want in your scene tree. Keep in mind that everything will be drawn relative to this node. 28 | 29 | Note that all updates to debug shapes happen in `_process()` with a very low priority, so you should typically be able to call drawing functions from anywhere. Under some circumstances, such as calling drawing functions in quick succession faster than `_process()` delta times, duplicate shapes may appear. 30 | 31 | 32 | ## Drawing functions reference 33 | ```gdscript 34 | draw_debug_cube(t: float, p: Vector3, extents: Vector3, c: Color = Color(0, 0, 0), b_triangles: bool = false) 35 | ``` 36 | * t: display time, will last a single frame if equal to 0 (ideal for continuously updating shapes) 37 | * p: center of the box 38 | * extents: dimensions of the box 39 | * c: color of the shape 40 | * b_triangles: displays as solid shape if true, otherwise only draws wireframe 41 | 42 | ```gdscript 43 | draw_debug_sphere(t: float, p: Vector3, lon: int, lat: int, r: float, c: Color = Color(0, 0, 0), b_triangles: bool = false) 44 | ``` 45 | * p: center of the sphere 46 | * lon: longitude subdivisions, lon = 8 will look like an octogon when seen from above 47 | * lat: latitude subdivision, half the value of `lon` will give the same number of subdivisions 48 | * r: radius of the sphere 49 | 50 | ```gdscript 51 | draw_debug_cylinder(t: float, p1: Vector3, p2: Vector3, r: float, lon: int = 8, b_caps: bool = true, c: Color = Color(0, 0, 0), b_triangles: bool = false) 52 | ``` 53 | * p1: first end of the cylinder 54 | * p2: second end of the cylinder 55 | * r: radius of the cylinder 56 | * lon: number of sides (see description for debug_sphere's `lon`) 57 | * b_caps: draw flat ends of the cylinders 58 | 59 | ```gdscript 60 | draw_debug_cone(t: float, p1: Vector3, p2: Vector3, r1: float, r2: float, lon: int = 8, b_caps: bool = true, c: Color = Color(0, 0, 0), b_triangles: bool = false) 61 | ``` 62 | * r1: radius at first end 63 | * r2: radius at second end 64 | * All other parameters are the same as debug_cylinder 65 | 66 | ```gdscript 67 | draw_debug_arrow(t: float, p: Vector3, n: Vector3, s: float = 1.0, c: Color = Color(0, 0, 0), b_triangles: bool = true) 68 | ``` 69 | * p: origin of the arrow 70 | * n: direction of the arrow 71 | * s: size (length) of the arrow 72 | 73 | ```gdscript 74 | draw_debug_coordinate_system(t: float, p: Vector3, x: Vector3 = Vector3.RIGHT, y: Vector3 = Vector3.UP, s: float = 1.0, c: float = 1.0, b_triangles: bool = true) 75 | ``` 76 | * p: origin of the coordinate system 77 | * x: direction of the X axis 78 | * y: direction of the Y axis (this vector defines the XY plane, not the Y axis itself) 79 | * s: size of the arrows 80 | 81 | ```gdscript 82 | draw_debug_grid(t: float, p: Vector3, a: float, b: float, div_a: int, div_b: int, normal: Vector3 = Vector3(0, 1, 0), tangent: Vector3 = Vector3(1, 0, 0), color: Color = Color(0, 0, 0)) 83 | ``` 84 | * p: center of the grid 85 | * a: length of the grid 86 | * b: width of the grid 87 | * div_a: number of divisions along length, must be at least 1 to render 88 | * div_b: number of division along width, must be at least 1 to render 89 | * normal: normal vector to the plane of the grid 90 | * tangent: defines the direction of the 'a' direction 91 | 92 | ```gdscript 93 | draw_debug_line(t: float, p1: Vector3, p2: Vector3, thickness: float, color: Color = Color(0, 0, 0)) 94 | ``` 95 | * p1: first end of the line 96 | * p2: second end of the line 97 | * thickness: if greater than zero, represents the diameter of the corresponding cylinder 98 | 99 | ```gdscript 100 | draw_debug_point(t: float, p: Vector3, size: float, color: Color = Color(0, 0, 0), b_triangles: bool = true) 101 | ``` 102 | * p: position of the point 103 | * size: if greater than zero, represents the diameter of the corresponding sphere. Note that a size of zero may render as a single pixel. 104 | -------------------------------------------------------------------------------- /debug_geometry.gd: -------------------------------------------------------------------------------- 1 | extends MeshInstance3D 2 | 3 | 4 | var im := ImmediateMesh.new() 5 | var mat := StandardMaterial3D.new() 6 | 7 | var debug_shapes: Array[DebugShape] = [] 8 | 9 | 10 | func _ready() -> void: 11 | mat.vertex_color_use_as_albedo = true 12 | mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED 13 | mesh = im 14 | 15 | process_priority = 1000 16 | 17 | 18 | func _process(delta: float) -> void: 19 | _update_geometry_timer(delta) 20 | 21 | 22 | func _update_geometry_timer(delta: float) -> void: 23 | _clear_geometry() 24 | _draw_geometry() 25 | if not debug_shapes.is_empty(): 26 | var count := debug_shapes.size() 27 | for i in count: 28 | var shape := debug_shapes[count - 1 - i] 29 | shape.draw_time -= delta 30 | if shape.draw_time < 0.0: 31 | debug_shapes.remove_at(count - 1 - i) 32 | 33 | 34 | func _clear_geometry() -> void: 35 | im.clear_surfaces() 36 | 37 | 38 | func _draw_geometry(index: int = 0) -> void: 39 | for i in range(index, debug_shapes.size()): 40 | var shape := debug_shapes[i] 41 | _draw_debug_shape(shape) 42 | 43 | 44 | func _draw_debug_shape(shape: DebugShape) -> void: 45 | if shape is DebugShapeCompound: 46 | for sub_shape in shape.debug_shapes: 47 | _draw_debug_shape(sub_shape) 48 | else: 49 | var primitives: Array[Primitive] = [] 50 | if shape.draw_surfaces: 51 | im.surface_begin(Mesh.PRIMITIVE_TRIANGLES, mat) 52 | primitives = shape.triangle_primitives 53 | else: 54 | im.surface_begin(Mesh.PRIMITIVE_LINES, mat) 55 | primitives = shape.line_primitives 56 | im.surface_set_color(shape.color) 57 | for primitive in primitives: 58 | for vertex in primitive.vertices: 59 | im.surface_add_vertex(vertex) 60 | im.surface_end() 61 | 62 | 63 | func draw_debug_cube(t: float, p: Vector3, extents: Vector3, c: Color = Color(0, 0, 0), b_triangles: bool = false) -> void: 64 | var cube := DebugCube.new(extents) 65 | cube.position = p 66 | cube.color = c 67 | cube.draw_surfaces = b_triangles 68 | cube.draw_time = t 69 | debug_shapes.append(cube) 70 | cube.update_geometry() 71 | 72 | 73 | func draw_debug_sphere(t: float, p: Vector3, lon: int, lat: int, r: float, 74 | c: Color = Color(0, 0, 0), b_triangles: bool = false) -> void: 75 | var sphere := DebugSphere.new(r, lon, lat) 76 | sphere.position = p 77 | sphere.color = c 78 | sphere.draw_surfaces = b_triangles 79 | sphere.draw_time = t 80 | debug_shapes.append(sphere) 81 | sphere.update_geometry() 82 | 83 | 84 | func draw_debug_cylinder(t: float, p1: Vector3, p2: Vector3, r: float, lon: int = 8, b_caps: bool = true, 85 | c: Color = Color(0, 0, 0), b_triangles: bool = false) -> void: 86 | var cylinder := DebugCylinder.new(p1, p2, r, lon) 87 | cylinder.color = c 88 | cylinder.draw_caps = b_caps 89 | cylinder.draw_surfaces = b_triangles 90 | cylinder.draw_time = t 91 | debug_shapes.append(cylinder) 92 | cylinder.update_geometry() 93 | 94 | 95 | func draw_debug_cone(t: float, p1: Vector3, p2: Vector3, r1: float, r2: float, lon: int = 8, 96 | b_caps: bool = true, c: Color = Color(0, 0, 0), b_triangles: bool = false) -> void: 97 | var cone := DebugCone.new(p1, p2, r1, r2, lon) 98 | cone.color = c 99 | cone.draw_caps = b_caps 100 | cone.draw_surfaces = b_triangles 101 | cone.draw_time = t 102 | debug_shapes.append(cone) 103 | cone.update_geometry() 104 | 105 | 106 | func draw_debug_arrow(t: float, p: Vector3, n: Vector3, s: float = 1.0, 107 | c: Color = Color(0, 0, 0), b_triangles: bool = true) -> void: 108 | var arrow := DebugArrow.new(p, n, s) 109 | arrow.color = c 110 | arrow.draw_surfaces = b_triangles 111 | arrow.draw_time = t 112 | debug_shapes.append(arrow) 113 | arrow.update_geometry() 114 | 115 | 116 | func draw_debug_coordinate_system(t: float, p: Vector3, x: Vector3 = Vector3.RIGHT, y: Vector3 = Vector3.UP, 117 | s: float = 1.0, c: float = 1.0, b_triangles: bool = true) -> void: 118 | var coordinate_system := DebugCoordinateSystem.new(x, y, s) 119 | coordinate_system.position = p 120 | coordinate_system.color_intensity = c 121 | coordinate_system.draw_surfaces = b_triangles 122 | coordinate_system.draw_time = t 123 | debug_shapes.append(coordinate_system) 124 | coordinate_system.update_geometry() 125 | 126 | 127 | func draw_debug_grid(t: float, p: Vector3, a: float, b: float, div_a: int, div_b: int, 128 | normal: Vector3 = Vector3(0, 1, 0), tangent: Vector3 = Vector3(1, 0, 0), color: Color = Color(0, 0, 0)) -> void: 129 | var grid := DebugGrid.new(a, b, div_a, div_b, normal, tangent) 130 | grid.position = p 131 | grid.color = color 132 | grid.draw_surfaces = false 133 | grid.draw_time = t 134 | debug_shapes.append(grid) 135 | grid.update_geometry() 136 | 137 | 138 | func draw_debug_line(t: float, p1: Vector3, p2: Vector3, thickness: float, color: Color = Color(0, 0, 0)) -> void: 139 | var line := DebugLine.new(p1, p2, thickness) 140 | line.color = color 141 | line.draw_time = t 142 | debug_shapes.append(line) 143 | line.update_geometry() 144 | 145 | 146 | func draw_debug_point(t: float, p: Vector3, size: float, color: Color = Color(0, 0, 0), b_triangles: bool = true) -> void: 147 | var point := DebugPoint.new(size) 148 | point.position = p 149 | point.color = color 150 | point.draw_surfaces = b_triangles 151 | point.draw_time = t 152 | debug_shapes.append(point) 153 | point.update_geometry() 154 | 155 | 156 | class Primitive: 157 | var vertex_count := 2 158 | var vertices: Array[Vector3] = [] : 159 | get: 160 | while vertices.size() < vertex_count: 161 | vertices.append(Vector3.ZERO) 162 | return vertices 163 | set(array): 164 | if array.size() > vertex_count: 165 | vertices = array.slice(0, vertex_count) 166 | else: 167 | while array.size() < vertex_count: 168 | array.append([Vector3.ZERO]) 169 | vertices = array 170 | 171 | func _init(_vertices: Array[Vector3]) -> void: 172 | vertices = _vertices 173 | 174 | class LinePrimitive extends Primitive: 175 | func _init(_vertices: Array[Vector3]) -> void: 176 | vertex_count = 2 177 | super._init(_vertices) 178 | 179 | class TrianglePrimitive extends Primitive: 180 | func _init(_vertices: Array[Vector3]) -> void: 181 | vertex_count = 3 182 | super._init(_vertices) 183 | 184 | class DebugShape: 185 | var line_primitives: Array[LinePrimitive] = [] 186 | var triangle_primitives: Array[TrianglePrimitive] = [] 187 | 188 | var position := Vector3.ZERO 189 | var color := Color(0, 0, 0) 190 | var draw_surfaces := false 191 | var draw_time := 0.0 192 | 193 | func _draw_debug_shape() -> void: 194 | line_primitives.clear() 195 | triangle_primitives.clear() 196 | 197 | func _to_string() -> String: 198 | return "DebugShape" 199 | 200 | func add_line_primitive(vertices: Array[Vector3]) -> void: 201 | line_primitives.append(LinePrimitive.new(vertices)) 202 | 203 | func add_triangle_primitive(vertices: Array[Vector3]) -> void: 204 | triangle_primitives.append(TrianglePrimitive.new(vertices)) 205 | 206 | func set_draw_time(time: float) -> void: 207 | draw_time = time 208 | 209 | func update_geometry() -> void: 210 | _draw_debug_shape() 211 | 212 | class DebugShapeCompound extends DebugShape: 213 | var debug_shapes: Array[DebugShape] = [] 214 | 215 | func _draw_debug_shape() -> void: 216 | for shape in debug_shapes: 217 | shape._draw_debug_shape() 218 | 219 | func _to_string() -> String: 220 | return "DebugShapeCompound" 221 | 222 | func add_debug_shape(shape: DebugShape) -> void: 223 | debug_shapes.append(shape) 224 | 225 | func set_draw_time(time: float) -> void: 226 | for shape in debug_shapes: 227 | shape.set_draw_time(time) 228 | 229 | class DebugCube extends DebugShape: 230 | var extents := Vector3.ONE 231 | 232 | func _init(size: Vector3) -> void: 233 | extents = size 234 | 235 | func _draw_debug_shape() -> void: 236 | super() 237 | 238 | var x := extents.x 239 | var y := extents.y 240 | var z := extents.z 241 | var points := [0.5 * Vector3(-x, -y, -z) + position, 242 | 0.5 * Vector3(-x, -y, z) + position, 243 | 0.5 * Vector3(-x, y, -z) + position, 244 | 0.5 * Vector3(-x, y, z) + position, 245 | 0.5 * Vector3(x, -y, -z) + position, 246 | 0.5 * Vector3(x, -y, z) + position, 247 | 0.5 * Vector3(x, y, -z) + position, 248 | 0.5 * Vector3(x, y, z) + position] 249 | if draw_surfaces: 250 | add_triangle_primitive([points[0], points[2], points[3]]) 251 | add_triangle_primitive([points[3], points[1], points[0]]) 252 | add_triangle_primitive([points[4], points[5], points[7]]) 253 | add_triangle_primitive([points[7], points[6], points[4]]) 254 | add_triangle_primitive([points[0], points[1], points[5]]) 255 | add_triangle_primitive([points[5], points[4], points[0]]) 256 | add_triangle_primitive([points[3], points[2], points[7]]) 257 | add_triangle_primitive([points[7], points[2], points[6]]) 258 | add_triangle_primitive([points[0], points[4], points[2]]) 259 | add_triangle_primitive([points[2], points[4], points[6]]) 260 | add_triangle_primitive([points[1], points[3], points[7]]) 261 | add_triangle_primitive([points[7], points[5], points[1]]) 262 | else: 263 | add_line_primitive([points[0], points[1]]) 264 | add_line_primitive([points[1], points[3]]) 265 | add_line_primitive([points[3], points[2]]) 266 | add_line_primitive([points[2], points[0]]) 267 | add_line_primitive([points[4], points[5]]) 268 | add_line_primitive([points[5], points[7]]) 269 | add_line_primitive([points[7], points[6]]) 270 | add_line_primitive([points[6], points[4]]) 271 | add_line_primitive([points[0], points[4]]) 272 | add_line_primitive([points[1], points[5]]) 273 | add_line_primitive([points[3], points[7]]) 274 | add_line_primitive([points[2], points[6]]) 275 | 276 | func _to_string() -> String: 277 | return "DebugCube" 278 | 279 | class DebugSphere extends DebugShape: 280 | var longitude := 36 : 281 | set(value): 282 | longitude = clampi(value, 0, 72) 283 | var latitude := 18 : 284 | set(value): 285 | latitude = clampi(value, 0, 36) 286 | var radius := 0.5 287 | 288 | func _init(rad: float, lon: int = 36, lat: int = 18) -> void: 289 | radius = rad 290 | longitude = lon 291 | latitude = lat 292 | 293 | func _draw_debug_shape() -> void: 294 | super() 295 | 296 | for i in range(1, latitude + 1): 297 | var lat0 := PI * (-0.5 + (i - 1) / (latitude as float)) 298 | var y0 := sin(lat0) 299 | var r0 := cos(lat0) 300 | var lat1 := PI * (-0.5 + i / (latitude as float)) 301 | var y1 := sin(lat1) 302 | var r1 := cos(lat1) 303 | for j in range(1, longitude + 1): 304 | var lon0 := 2 * PI * ((j - 1) / (longitude as float)) 305 | var x0 := cos(lon0) 306 | var z0 := sin(lon0) 307 | var lon1 := 2 * PI * (j / (longitude as float)) 308 | var x1 := cos(lon1) 309 | var z1 := sin(lon1) 310 | 311 | var points := [radius * Vector3(x1 * r0, y0, z1 * r0) + position, 312 | radius * Vector3(x1 * r1, y1, z1 * r1) + position, 313 | radius * Vector3(x0 * r1, y1, z0 * r1) + position, 314 | radius * Vector3(x0 * r0, y0, z0 * r0) + position] 315 | 316 | if draw_surfaces: 317 | add_triangle_primitive([points[0], points[1], points[2]]) 318 | add_triangle_primitive([points[2], points[3], points[0]]) 319 | else: 320 | add_line_primitive([points[0], points[1]]) 321 | add_line_primitive([points[1], points[2]]) 322 | 323 | func _to_string() -> String: 324 | return "DebugSphere" 325 | 326 | class DebugCone extends DebugShape: 327 | var longitude := 8 328 | var radius_top := 0.5 329 | var radius_base := 0.5 330 | var base := Vector3.ZERO 331 | var top := Vector3.ZERO 332 | var draw_caps := false 333 | 334 | func _init(p1: Vector3, p2: Vector3, r1: float, r2: float, sides: int = 8) -> void: 335 | base = p1 336 | top = p2 337 | radius_base = r1 338 | radius_top = r2 339 | longitude = sides 340 | 341 | func _draw_debug_shape() -> void: 342 | super() 343 | 344 | position = (top + base) / 2 345 | var height := (top - base).length() 346 | for i in range(1, longitude + 1): 347 | var lon0 := 2 * PI * ((i - 1) as float / longitude) 348 | var x0 := cos(lon0) 349 | var z0 := sin(lon0) 350 | var lon1 := 2 * PI * (i as float / longitude) 351 | var x1 := cos(lon1) 352 | var z1 := sin(lon1) 353 | 354 | var points := [Vector3(x0 * radius_base, 0, z0 * radius_base), 355 | Vector3(x0 * radius_top, height, z0 * radius_top), 356 | Vector3(x1 * radius_base, 0, z1 * radius_base), 357 | Vector3(x1 * radius_top, height, z1 * radius_top), 358 | Vector3(0.0, 0, 0.0), 359 | Vector3(0.0, height, 0.0)] 360 | 361 | var dir := (top - base).normalized() 362 | var rot := Vector3.RIGHT 363 | var ang := 0.0 364 | if dir == Vector3.DOWN: 365 | ang = PI 366 | elif dir != Vector3.UP and dir != Vector3.ZERO: 367 | rot = Vector3.UP.cross(dir).normalized() 368 | ang = Vector3.UP.angle_to(dir) 369 | for j in range(points.size()): 370 | points[j] = points[j].rotated(rot, ang) + base 371 | 372 | if draw_surfaces: 373 | add_triangle_primitive([points[0], points[2], points[1]]) 374 | add_triangle_primitive([points[1], points[2], points[3]]) 375 | if draw_caps: 376 | add_triangle_primitive([points[0], points[4], points[2]]) 377 | add_triangle_primitive([points[1], points[3], points[5]]) 378 | else: 379 | add_line_primitive([points[0], points[1]]) 380 | add_line_primitive([points[1], points[3]]) 381 | add_line_primitive([points[2], points[0]]) 382 | if draw_caps: 383 | add_line_primitive([points[0], points[4]]) 384 | add_line_primitive([points[1], points[5]]) 385 | 386 | func _to_string() -> String: 387 | return "DebugCone" 388 | 389 | class DebugCylinder extends DebugCone: 390 | func _init(p1: Vector3, p2: Vector3, rad: float, lon: int = 8) -> void: 391 | super(p1, p2, rad, rad, lon) 392 | 393 | func _to_string() -> String: 394 | return "DebugCylinder" 395 | 396 | class DebugArrow extends DebugShapeCompound: 397 | var direction := Vector3.ONE 398 | var length := 1.0 399 | 400 | func _init(pos: Vector3, dir: Vector3, size: float) -> void: 401 | position = pos 402 | direction = dir 403 | length = size 404 | 405 | func _draw_debug_shape() -> void: 406 | debug_shapes.clear() 407 | 408 | direction = direction.normalized() 409 | var arrow_end_local := direction * length 410 | var arrow_body := DebugCylinder.new( 411 | position, position + 0.8 * arrow_end_local, 0.05 * length, 8) 412 | arrow_body.color = color 413 | arrow_body.draw_surfaces = draw_surfaces 414 | arrow_body.draw_caps = true 415 | var arrow_tip := DebugCone.new( 416 | position + 0.8 * arrow_end_local, position + arrow_end_local, 0.1 * length, 0, 8) 417 | arrow_tip.color = color 418 | arrow_tip.draw_surfaces = draw_surfaces 419 | arrow_tip.draw_caps = true 420 | add_debug_shape(arrow_body) 421 | add_debug_shape(arrow_tip) 422 | 423 | super() 424 | 425 | func _to_string() -> String: 426 | return "DebugArrow" 427 | 428 | class DebugCoordinateSystem extends DebugShapeCompound: 429 | var size := 1.0 430 | var dir_x := Vector3.RIGHT 431 | var dir_y := Vector3.UP 432 | var color_intensity := 1.0 433 | 434 | func _init(x: Vector3 = Vector3.RIGHT, y: Vector3 = Vector3.UP, s: float = 1.0) -> void: 435 | dir_x = x 436 | dir_y = y 437 | size = s 438 | 439 | func _draw_debug_shape() -> void: 440 | debug_shapes.clear() 441 | 442 | dir_x = dir_x.normalized() 443 | var dir_z = dir_x.cross(dir_y).normalized() 444 | dir_y = dir_z.cross(dir_x).normalized() 445 | 446 | color_intensity = clamp(color_intensity, 0, 10) 447 | var arrow_x := DebugArrow.new(position, dir_x, size) 448 | var arrow_y := DebugArrow.new(position, dir_y, size) 449 | var arrow_z := DebugArrow.new(position, dir_z, size) 450 | arrow_x.draw_surfaces = draw_surfaces 451 | arrow_y.draw_surfaces = draw_surfaces 452 | arrow_z.draw_surfaces = draw_surfaces 453 | arrow_x.color = Color(color_intensity, 0, 0) 454 | arrow_y.color = Color(0, color_intensity, 0) 455 | arrow_z.color = Color(0, 0, color_intensity) 456 | add_debug_shape(arrow_x) 457 | add_debug_shape(arrow_y) 458 | add_debug_shape(arrow_z) 459 | 460 | super() 461 | 462 | func _to_string() -> String: 463 | return "DebugCoordinateSystem" 464 | 465 | class DebugGrid extends DebugShape: 466 | var length := 5.0 467 | var width := 5.0 468 | var length_divs := 6 : 469 | set(value): 470 | length_divs = 1 if value < 1 else value 471 | var width_divs := 6 : 472 | set(value): 473 | width_divs = 1 if value < 1 else value 474 | var normal_direction := Vector3(0, 1, 0) 475 | var tangent_direction := Vector3(1, 0, 0) 476 | 477 | func _init(a: float, b: float, div_a: int, div_b: int, 478 | normal: Vector3 = Vector3(0, 1, 0), tangent: Vector3 = Vector3(1, 0, 0)) -> void: 479 | length = a 480 | width = b 481 | length_divs = div_a 482 | width_divs = div_b 483 | normal_direction = normal 484 | tangent_direction = tangent 485 | 486 | func _draw_debug_shape() -> void: 487 | super() 488 | 489 | if tangent_direction == normal_direction: 490 | tangent_direction = Vector3.RIGHT 491 | 492 | var normal_rotation := Vector3.RIGHT 493 | var normal_angle := 0.0 494 | if normal_direction == Vector3.DOWN: 495 | normal_angle = PI 496 | elif normal_direction != Vector3.UP and normal_direction != Vector3.ZERO: 497 | normal_rotation = Vector3.UP.cross(normal_direction).normalized() 498 | normal_angle = Vector3.UP.angle_to(normal_direction) 499 | if normal_direction.cross(Vector3.UP).normalized() == -normal_direction.normalized(): 500 | normal_angle = -normal_angle 501 | var rotated_right_vector := Vector3.RIGHT.rotated(normal_rotation, normal_angle) 502 | var tangent_rotation := normal_direction.normalized() 503 | var projection := tangent_direction - tangent_direction.dot(normal_direction) / normal_direction.length_squared() * normal_direction 504 | var tangent_angle := rotated_right_vector.angle_to(projection) 505 | if rotated_right_vector.cross(projection).normalized() != normal_direction.normalized(): 506 | tangent_angle = -tangent_angle 507 | 508 | for i in range(0, length_divs + 1): 509 | var lx := length * (i as float / length_divs - 0.5) 510 | add_line_primitive([Vector3(lx, 0, -width / 2.0).rotated(normal_rotation, normal_angle).rotated( 511 | tangent_rotation, tangent_angle) + position, 512 | Vector3(lx, 0, width / 2.0).rotated(normal_rotation, normal_angle).rotated( 513 | tangent_rotation, tangent_angle) + position]) 514 | for j in range(0, width_divs + 1): 515 | var lz := width * (j as float / width_divs - 0.5) 516 | add_line_primitive([Vector3(-length / 2.0, 0, lz).rotated(normal_rotation, normal_angle).rotated( 517 | tangent_rotation, tangent_angle) + position, 518 | Vector3(length / 2.0, 0, lz).rotated(normal_rotation, normal_angle).rotated( 519 | tangent_rotation, tangent_angle) + position]) 520 | 521 | func _to_string() -> String: 522 | return "DebugGrid" 523 | 524 | class DebugLine extends DebugCylinder: 525 | var line_thickness := 0.0 526 | 527 | func _init(p1: Vector3, p2: Vector3, thickness: float) -> void: 528 | super(p1, p2, thickness, 8) 529 | base = p1 530 | top = p2 531 | line_thickness = thickness 532 | 533 | func _draw_debug_shape() -> void: 534 | if line_thickness <= 0.0: 535 | draw_surfaces = false 536 | add_line_primitive([base, top]) 537 | else: 538 | draw_surfaces = true 539 | super() 540 | 541 | func _to_string() -> String: 542 | return "DebugLine" 543 | 544 | class DebugPoint extends DebugSphere: 545 | func _init(size: float) -> void: 546 | super(size / 2, 8, 4) 547 | if size < 0.005: 548 | radius = 0.005 549 | 550 | func _draw_debug_shape() -> void: 551 | super() 552 | 553 | func _to_string() -> String: 554 | return "DebugPoint" 555 | --------------------------------------------------------------------------------