• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 19715474985

26 Nov 2025 07:39PM UTC coverage: 88.641% (+0.07%) from 88.576%
19715474985

Pull #3585

github

web-flow
Merge 41c7b13ed into 3b683c589
Pull Request #3585: feat!: debug draw improvements

5289 of 7219 branches covered (73.26%)

280 of 306 new or added lines in 18 files covered. (91.5%)

4 existing lines in 2 files now uncovered.

14655 of 16533 relevant lines covered (88.64%)

24577.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.91
/src/engine/Graphics/Context/debug-line-renderer/debug-line-renderer.ts
1
import type { Vector } from '../../../Math/vector';
2
import { vec } from '../../../Math/vector';
3
import type { Color } from '../../../Color';
4
import lineVertexSource from './debug-line-vertex.glsl?raw';
5
import lineFragmentSource from './debug-line-fragment.glsl?raw';
6
import type { ExcaliburGraphicsContextWebGL } from '../ExcaliburGraphicsContextWebGL';
7
import type { RendererPlugin } from '../renderer';
8
import { Shader, VertexBuffer, VertexLayout } from '../..';
9
import { GraphicsDiagnostics } from '../../GraphicsDiagnostics';
10

11
export interface LineOptions {
12
  color?: Color;
13
  width?: number;
14
}
15

16
export class DebugLineRenderer implements RendererPlugin {
17
  public readonly type = 'ex.debug-line';
12✔
18
  public priority: number = 0;
12✔
19
  private _context!: ExcaliburGraphicsContextWebGL;
20
  private _gl!: WebGL2RenderingContext;
21
  private _shader!: Shader;
22
  private _maxLines: number = 10922;
12✔
23
  private _vertexBuffer!: VertexBuffer;
24
  private _layout!: VertexLayout;
25
  private _vertexIndex = 0;
12✔
26
  private _lineCount = 0;
12✔
27
  initialize(gl: WebGL2RenderingContext, context: ExcaliburGraphicsContextWebGL): void {
28
    this._gl = gl;
12✔
29
    this._context = context;
12✔
30
    this._shader = new Shader({
12✔
31
      graphicsContext: context,
32
      vertexSource: lineVertexSource,
33
      fragmentSource: lineFragmentSource
34
    });
35
    this._shader.compile();
12✔
36
    this._shader.use();
12✔
37

38
    this._shader.setUniformMatrix('u_matrix', this._context.ortho);
12✔
39

40
    this._vertexBuffer = new VertexBuffer({
12✔
41
      gl,
42
      size: 7 * 6 * this._maxLines, // 7 floats per vert, 6 verts per line
43
      type: 'dynamic'
44
    });
45

46
    this._layout = new VertexLayout({
12✔
47
      gl,
48
      vertexBuffer: this._vertexBuffer,
49
      shader: this._shader,
50
      attributes: [
51
        ['a_position', 2],
52
        ['a_color', 4],
53
        ['a_lengthSoFar', 1]
54
      ]
55
    });
56
  }
57

58
  public dispose() {
59
    this._vertexBuffer.dispose();
12✔
60
    this._shader.dispose();
12✔
61
    this._context = null as any;
12✔
62
    this._gl = null as any;
12✔
63
  }
64

65
  private _startScratch = vec(0, 0);
12✔
66
  private _endScratch = vec(0, 0);
12✔
67
  private _lengthSoFar = 0;
12✔
68
  private _currentlyDashed = false;
12✔
69
  draw(start: Vector, end: Vector, color: Color, width = 2, dashed: boolean = false): void {
889✔
70
    // Force a render if the batch is full or we switch form dashed -> not dashed or vice versa
71
    if (this._isFull() || this._currentlyDashed !== dashed) {
471✔
72
      this._currentlyDashed = dashed;
2✔
73
      this.flush();
2✔
74
    }
75

76
    this._lineCount++;
471✔
77

78
    const transform = this._context.getTransform();
471✔
79
    const finalStart = transform.multiply(start, this._startScratch);
471✔
80
    const finalEnd = transform.multiply(end, this._endScratch);
471✔
81

82
    const dir = finalEnd.sub(finalStart);
471✔
83
    const dist = finalStart.distance(finalEnd);
471✔
84
    const normal = dir.normal();
471✔
85
    const halfWidth = width / 2;
471✔
86

87
    const vertexBuffer = this._vertexBuffer.bufferData;
471✔
88
    // Start Bottom Vert
89
    vertexBuffer[this._vertexIndex++] = finalStart.x - normal.x * halfWidth;
471✔
90
    vertexBuffer[this._vertexIndex++] = finalStart.y - normal.y * halfWidth;
471✔
91
    vertexBuffer[this._vertexIndex++] = color.r / 255;
471✔
92
    vertexBuffer[this._vertexIndex++] = color.g / 255;
471✔
93
    vertexBuffer[this._vertexIndex++] = color.b / 255;
471✔
94
    vertexBuffer[this._vertexIndex++] = color.a;
471✔
95
    vertexBuffer[this._vertexIndex++] = this._lengthSoFar;
471✔
96

97
    // Start Top Vert
98
    vertexBuffer[this._vertexIndex++] = finalStart.x + normal.x * halfWidth;
471✔
99
    vertexBuffer[this._vertexIndex++] = finalStart.y + normal.y * halfWidth;
471✔
100
    vertexBuffer[this._vertexIndex++] = color.r / 255;
471✔
101
    vertexBuffer[this._vertexIndex++] = color.g / 255;
471✔
102
    vertexBuffer[this._vertexIndex++] = color.b / 255;
471✔
103
    vertexBuffer[this._vertexIndex++] = color.a;
471✔
104
    vertexBuffer[this._vertexIndex++] = this._lengthSoFar;
471✔
105

106
    // End Bottom Vert
107
    vertexBuffer[this._vertexIndex++] = finalEnd.x - normal.x * halfWidth;
471✔
108
    vertexBuffer[this._vertexIndex++] = finalEnd.y - normal.y * halfWidth;
471✔
109
    vertexBuffer[this._vertexIndex++] = color.r / 255;
471✔
110
    vertexBuffer[this._vertexIndex++] = color.g / 255;
471✔
111
    vertexBuffer[this._vertexIndex++] = color.b / 255;
471✔
112
    vertexBuffer[this._vertexIndex++] = color.a;
471✔
113
    vertexBuffer[this._vertexIndex++] = this._lengthSoFar + dist;
471✔
114

115
    // End Bottom Vert
116
    vertexBuffer[this._vertexIndex++] = finalEnd.x - normal.x * halfWidth;
471✔
117
    vertexBuffer[this._vertexIndex++] = finalEnd.y - normal.y * halfWidth;
471✔
118
    vertexBuffer[this._vertexIndex++] = color.r / 255;
471✔
119
    vertexBuffer[this._vertexIndex++] = color.g / 255;
471✔
120
    vertexBuffer[this._vertexIndex++] = color.b / 255;
471✔
121
    vertexBuffer[this._vertexIndex++] = color.a;
471✔
122
    vertexBuffer[this._vertexIndex++] = this._lengthSoFar + dist;
471✔
123

124
    // Start Top Vert
125
    vertexBuffer[this._vertexIndex++] = finalStart.x + normal.x * halfWidth;
471✔
126
    vertexBuffer[this._vertexIndex++] = finalStart.y + normal.y * halfWidth;
471✔
127
    vertexBuffer[this._vertexIndex++] = color.r / 255;
471✔
128
    vertexBuffer[this._vertexIndex++] = color.g / 255;
471✔
129
    vertexBuffer[this._vertexIndex++] = color.b / 255;
471✔
130
    vertexBuffer[this._vertexIndex++] = color.a;
471✔
131
    vertexBuffer[this._vertexIndex++] = this._lengthSoFar;
471✔
132

133
    // End Top Vert
134
    vertexBuffer[this._vertexIndex++] = finalEnd.x + normal.x * halfWidth;
471✔
135
    vertexBuffer[this._vertexIndex++] = finalEnd.y + normal.y * halfWidth;
471✔
136
    vertexBuffer[this._vertexIndex++] = color.r / 255;
471✔
137
    vertexBuffer[this._vertexIndex++] = color.g / 255;
471✔
138
    vertexBuffer[this._vertexIndex++] = color.b / 255;
471✔
139
    vertexBuffer[this._vertexIndex++] = color.a;
471✔
140
    vertexBuffer[this._vertexIndex++] = this._lengthSoFar + dist;
471✔
141
  }
142

143
  private _isFull() {
144
    if (this._lineCount >= this._maxLines) {
471!
NEW
145
      return true;
×
146
    }
147
    return false;
471✔
148
  }
149

150
  hasPendingDraws(): boolean {
151
    return this._lineCount !== 0;
8✔
152
  }
153

154
  flush(): void {
155
    // nothing to draw early exit
156
    if (this._lineCount === 0) {
14✔
157
      return;
2✔
158
    }
159

160
    const gl = this._gl;
12✔
161
    this._shader.use();
12✔
162
    this._layout.use(true);
12✔
163

164
    this._shader.setUniformMatrix('u_matrix', this._context.ortho);
12✔
165
    this._shader.setUniformBoolean('u_dashed', this._currentlyDashed);
12✔
166

167
    gl.drawArrays(gl.TRIANGLES, 0, this._lineCount * 6); // 6 verts per line
12✔
168

169
    GraphicsDiagnostics.DrawnImagesCount += this._lineCount;
12✔
170
    GraphicsDiagnostics.DrawCallCount++;
12✔
171

172
    // reset
173
    this._vertexIndex = 0;
12✔
174
    this._lineCount = 0;
12✔
175
    this._lengthSoFar = 0;
12✔
176
  }
177
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc