@forgeax/engine-debug-draw 0.0.0-dev.8d955ade1c79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +256 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/errors.test-d.d.ts +2 -0
- package/dist/__tests__/errors.test-d.d.ts.map +1 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/debug-draw.d.ts +64 -0
- package/dist/debug-draw.d.ts.map +1 -0
- package/dist/errors.d.ts +65 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +893 -0
- package/dist/index.mjs.map +1 -0
- package/dist/render-feature.d.ts +12 -0
- package/dist/render-feature.d.ts.map +1 -0
- package/dist/shapes/aabb.d.ts +4 -0
- package/dist/shapes/aabb.d.ts.map +1 -0
- package/dist/shapes/arrow.d.ts +11 -0
- package/dist/shapes/arrow.d.ts.map +1 -0
- package/dist/shapes/axes.d.ts +18 -0
- package/dist/shapes/axes.d.ts.map +1 -0
- package/dist/shapes/frustum.d.ts +7 -0
- package/dist/shapes/frustum.d.ts.map +1 -0
- package/dist/shapes/line.d.ts +4 -0
- package/dist/shapes/line.d.ts.map +1 -0
- package/dist/shapes/sphere.d.ts +7 -0
- package/dist/shapes/sphere.d.ts.map +1 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +62 -0
- package/src/__tests__/errors.test-d.ts +89 -0
- package/src/constants.ts +13 -0
- package/src/debug-draw.ts +709 -0
- package/src/errors.ts +142 -0
- package/src/index.ts +18 -0
- package/src/render-feature.ts +73 -0
- package/src/shapes/aabb.ts +57 -0
- package/src/shapes/arrow.ts +75 -0
- package/src/shapes/axes.ts +60 -0
- package/src/shapes/frustum.ts +182 -0
- package/src/shapes/line.ts +14 -0
- package/src/shapes/sphere.ts +74 -0
- package/src/types.ts +174 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
import { ok, err } from '@forgeax/engine-types';
|
|
2
|
+
import { mat4, vec3, quat } from '@forgeax/engine-math';
|
|
3
|
+
|
|
4
|
+
// src/constants.ts
|
|
5
|
+
var INITIAL_VERTEX_CAPACITY = 1024;
|
|
6
|
+
var MAX_VERTEX_CAPACITY = 1e6;
|
|
7
|
+
var VERTEX_STRIDE_BYTES = 16;
|
|
8
|
+
function makeError(code, expected, hint, detail) {
|
|
9
|
+
const error = {
|
|
10
|
+
code,
|
|
11
|
+
expected,
|
|
12
|
+
hint,
|
|
13
|
+
detail,
|
|
14
|
+
get message() {
|
|
15
|
+
return `[${code}] ${hint}`;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return error;
|
|
19
|
+
}
|
|
20
|
+
function pipelineCreateFailed(rhiError) {
|
|
21
|
+
return err(
|
|
22
|
+
makeError(
|
|
23
|
+
"pipeline-create-failed",
|
|
24
|
+
"PSO creation should succeed with valid WGSL + layout",
|
|
25
|
+
`Pipeline creation failed: ${rhiError}. Check WGSL syntax, vertex layout, and depth-stencil state.`,
|
|
26
|
+
{ code: "pipeline-create-failed", rhiError }
|
|
27
|
+
)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function bufferAllocationFailed(rhiError) {
|
|
31
|
+
return err(
|
|
32
|
+
makeError(
|
|
33
|
+
"buffer-allocation-failed",
|
|
34
|
+
"GPU vertex buffer allocation should succeed for the requested byte size",
|
|
35
|
+
`Buffer allocation failed: ${rhiError}. Check available device memory and buffer usage flags.`,
|
|
36
|
+
{ code: "buffer-allocation-failed", rhiError }
|
|
37
|
+
)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
function flushedAfterDestroy() {
|
|
41
|
+
return err(
|
|
42
|
+
makeError(
|
|
43
|
+
"flushed-after-destroy",
|
|
44
|
+
"DebugDraw instance is alive and not yet destroyed",
|
|
45
|
+
"DebugDraw was destroyed; create a new instance via createDebugDraw().",
|
|
46
|
+
{ code: "flushed-after-destroy" }
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
function viewProjRequired() {
|
|
51
|
+
return err(
|
|
52
|
+
makeError(
|
|
53
|
+
"viewProj-required",
|
|
54
|
+
"viewProj must be provided as a Mat4 for flush to transform vertices",
|
|
55
|
+
"Pass a viewProj Mat4 to flush(encoder, view, viewProj).",
|
|
56
|
+
{ code: "viewProj-required" }
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/shapes/aabb.ts
|
|
62
|
+
function a(v, i) {
|
|
63
|
+
return v[i];
|
|
64
|
+
}
|
|
65
|
+
function aabbVertices(min, max) {
|
|
66
|
+
const mnx = a(min, 0);
|
|
67
|
+
const mny = a(min, 1);
|
|
68
|
+
const mnz = a(min, 2);
|
|
69
|
+
const mxx = a(max, 0);
|
|
70
|
+
const mxy = a(max, 1);
|
|
71
|
+
const mxz = a(max, 2);
|
|
72
|
+
const c = [
|
|
73
|
+
[mnx, mny, mnz],
|
|
74
|
+
// 0
|
|
75
|
+
[mxx, mny, mnz],
|
|
76
|
+
// 1
|
|
77
|
+
[mnx, mxy, mnz],
|
|
78
|
+
// 2
|
|
79
|
+
[mxx, mxy, mnz],
|
|
80
|
+
// 3
|
|
81
|
+
[mnx, mny, mxz],
|
|
82
|
+
// 4
|
|
83
|
+
[mxx, mny, mxz],
|
|
84
|
+
// 5
|
|
85
|
+
[mnx, mxy, mxz],
|
|
86
|
+
// 6
|
|
87
|
+
[mxx, mxy, mxz]
|
|
88
|
+
// 7
|
|
89
|
+
];
|
|
90
|
+
const edges = [
|
|
91
|
+
[0, 1],
|
|
92
|
+
[0, 2],
|
|
93
|
+
[1, 3],
|
|
94
|
+
[2, 3],
|
|
95
|
+
[4, 5],
|
|
96
|
+
[4, 6],
|
|
97
|
+
[5, 7],
|
|
98
|
+
[6, 7],
|
|
99
|
+
[0, 4],
|
|
100
|
+
[1, 5],
|
|
101
|
+
[2, 6],
|
|
102
|
+
[3, 7]
|
|
103
|
+
];
|
|
104
|
+
const result = [];
|
|
105
|
+
for (const [ai, bi] of edges) {
|
|
106
|
+
const ac = c[ai];
|
|
107
|
+
const bc = c[bi];
|
|
108
|
+
result.push([ac[0], ac[1], ac[2]]);
|
|
109
|
+
result.push([bc[0], bc[1], bc[2]]);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
var TIP_DIRS = [
|
|
114
|
+
[-1, 1, 0],
|
|
115
|
+
[-1, 0, 1],
|
|
116
|
+
[-1, -1, 0],
|
|
117
|
+
[-1, 0, -1]
|
|
118
|
+
];
|
|
119
|
+
var UNIT_X = [1, 0, 0];
|
|
120
|
+
function arrowVertices(start, end, tipLength) {
|
|
121
|
+
const sx = start[0];
|
|
122
|
+
const sy = start[1];
|
|
123
|
+
const sz = start[2];
|
|
124
|
+
const ex = end[0];
|
|
125
|
+
const ey = end[1];
|
|
126
|
+
const ez = end[2];
|
|
127
|
+
const verts = [
|
|
128
|
+
[sx, sy, sz],
|
|
129
|
+
[ex, ey, ez]
|
|
130
|
+
];
|
|
131
|
+
const dir = vec3.create();
|
|
132
|
+
vec3.set(dir, ex - sx, ey - sy, ez - sz);
|
|
133
|
+
const len = vec3.length(dir);
|
|
134
|
+
if (len < 1e-6) return verts;
|
|
135
|
+
const headLen = tipLength ?? len / 10;
|
|
136
|
+
vec3.normalize(dir, dir);
|
|
137
|
+
const rot = quat.fromUnitVectors(quat.create(), UNIT_X, dir);
|
|
138
|
+
const tipLocal = vec3.create();
|
|
139
|
+
const tipWorld = vec3.create();
|
|
140
|
+
for (const [tx, ty, tz] of TIP_DIRS) {
|
|
141
|
+
vec3.set(tipLocal, tx, ty, tz);
|
|
142
|
+
vec3.normalize(tipLocal, tipLocal);
|
|
143
|
+
vec3.scale(tipLocal, tipLocal, headLen);
|
|
144
|
+
quat.transformVec3(tipWorld, rot, tipLocal);
|
|
145
|
+
verts.push([ex, ey, ez]);
|
|
146
|
+
verts.push([
|
|
147
|
+
ex + tipWorld[0],
|
|
148
|
+
ey + tipWorld[1],
|
|
149
|
+
ez + tipWorld[2]
|
|
150
|
+
]);
|
|
151
|
+
}
|
|
152
|
+
return verts;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/shapes/axes.ts
|
|
156
|
+
var AXES_COLORS = [
|
|
157
|
+
[1, 0, 0, 1],
|
|
158
|
+
[0, 1, 0, 1],
|
|
159
|
+
[0, 0, 1, 1]
|
|
160
|
+
];
|
|
161
|
+
function axesArrowSets(worldMat, length) {
|
|
162
|
+
const m = worldMat;
|
|
163
|
+
const ox = m[12];
|
|
164
|
+
const oy = m[13];
|
|
165
|
+
const oz = m[14];
|
|
166
|
+
const origin = [ox, oy, oz];
|
|
167
|
+
const cols = [
|
|
168
|
+
[m[0], m[1], m[2]],
|
|
169
|
+
[m[4], m[5], m[6]],
|
|
170
|
+
[m[8], m[9], m[10]]
|
|
171
|
+
];
|
|
172
|
+
const sets = [];
|
|
173
|
+
for (let i = 0; i < 3; i++) {
|
|
174
|
+
const c = cols[i];
|
|
175
|
+
const color = AXES_COLORS[i];
|
|
176
|
+
const end = [ox + c[0] * length, oy + c[1] * length, oz + c[2] * length];
|
|
177
|
+
sets.push({ vertices: arrowVertices(origin, end), color });
|
|
178
|
+
}
|
|
179
|
+
return sets;
|
|
180
|
+
}
|
|
181
|
+
function at(m, i) {
|
|
182
|
+
return m[i];
|
|
183
|
+
}
|
|
184
|
+
function frustumVertices(viewProj) {
|
|
185
|
+
const m = viewProj;
|
|
186
|
+
const det = at(m, 0) * (at(m, 5) * (at(m, 10) * at(m, 15) - at(m, 14) * at(m, 11)) - at(m, 9) * (at(m, 6) * at(m, 15) - at(m, 14) * at(m, 7)) + at(m, 13) * (at(m, 6) * at(m, 11) - at(m, 10) * at(m, 7))) - at(m, 4) * (at(m, 1) * (at(m, 10) * at(m, 15) - at(m, 14) * at(m, 11)) - at(m, 9) * (at(m, 2) * at(m, 15) - at(m, 14) * at(m, 3)) + at(m, 13) * (at(m, 2) * at(m, 11) - at(m, 10) * at(m, 3))) + at(m, 8) * (at(m, 1) * (at(m, 6) * at(m, 15) - at(m, 14) * at(m, 7)) - at(m, 5) * (at(m, 2) * at(m, 15) - at(m, 14) * at(m, 3)) + at(m, 13) * (at(m, 2) * at(m, 7) - at(m, 6) * at(m, 3))) - at(m, 12) * (at(m, 1) * (at(m, 6) * at(m, 11) - at(m, 10) * at(m, 7)) - at(m, 5) * (at(m, 2) * at(m, 11) - at(m, 10) * at(m, 3)) + at(m, 9) * (at(m, 2) * at(m, 7) - at(m, 6) * at(m, 3)));
|
|
187
|
+
if (Math.abs(det) < 1e-10) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const invDet = 1 / det;
|
|
191
|
+
const inv = mat4.create();
|
|
192
|
+
inv[0] = (at(m, 5) * (at(m, 10) * at(m, 15) - at(m, 14) * at(m, 11)) - at(m, 9) * (at(m, 6) * at(m, 15) - at(m, 14) * at(m, 7)) + at(m, 13) * (at(m, 6) * at(m, 11) - at(m, 10) * at(m, 7))) * invDet;
|
|
193
|
+
inv[1] = -(at(m, 1) * (at(m, 10) * at(m, 15) - at(m, 14) * at(m, 11)) - at(m, 9) * (at(m, 2) * at(m, 15) - at(m, 14) * at(m, 3)) + at(m, 13) * (at(m, 2) * at(m, 11) - at(m, 10) * at(m, 3))) * invDet;
|
|
194
|
+
inv[2] = (at(m, 1) * (at(m, 6) * at(m, 15) - at(m, 14) * at(m, 7)) - at(m, 5) * (at(m, 2) * at(m, 15) - at(m, 14) * at(m, 3)) + at(m, 13) * (at(m, 2) * at(m, 7) - at(m, 6) * at(m, 3))) * invDet;
|
|
195
|
+
inv[3] = -(at(m, 1) * (at(m, 6) * at(m, 11) - at(m, 10) * at(m, 7)) - at(m, 5) * (at(m, 2) * at(m, 11) - at(m, 10) * at(m, 3)) + at(m, 9) * (at(m, 2) * at(m, 7) - at(m, 6) * at(m, 3))) * invDet;
|
|
196
|
+
inv[4] = -(at(m, 4) * (at(m, 10) * at(m, 15) - at(m, 14) * at(m, 11)) - at(m, 8) * (at(m, 6) * at(m, 15) - at(m, 14) * at(m, 7)) + at(m, 12) * (at(m, 6) * at(m, 11) - at(m, 10) * at(m, 7))) * invDet;
|
|
197
|
+
inv[5] = (at(m, 0) * (at(m, 10) * at(m, 15) - at(m, 14) * at(m, 11)) - at(m, 8) * (at(m, 2) * at(m, 15) - at(m, 14) * at(m, 3)) + at(m, 12) * (at(m, 2) * at(m, 11) - at(m, 10) * at(m, 3))) * invDet;
|
|
198
|
+
inv[6] = -(at(m, 0) * (at(m, 6) * at(m, 15) - at(m, 14) * at(m, 7)) - at(m, 4) * (at(m, 2) * at(m, 15) - at(m, 14) * at(m, 3)) + at(m, 12) * (at(m, 2) * at(m, 7) - at(m, 6) * at(m, 3))) * invDet;
|
|
199
|
+
inv[7] = (at(m, 0) * (at(m, 6) * at(m, 11) - at(m, 10) * at(m, 7)) - at(m, 4) * (at(m, 2) * at(m, 11) - at(m, 10) * at(m, 3)) + at(m, 8) * (at(m, 2) * at(m, 7) - at(m, 6) * at(m, 3))) * invDet;
|
|
200
|
+
inv[8] = (at(m, 4) * (at(m, 9) * at(m, 15) - at(m, 13) * at(m, 11)) - at(m, 8) * (at(m, 5) * at(m, 15) - at(m, 13) * at(m, 7)) + at(m, 12) * (at(m, 5) * at(m, 11) - at(m, 9) * at(m, 7))) * invDet;
|
|
201
|
+
inv[9] = -(at(m, 0) * (at(m, 9) * at(m, 15) - at(m, 13) * at(m, 11)) - at(m, 8) * (at(m, 1) * at(m, 15) - at(m, 13) * at(m, 3)) + at(m, 12) * (at(m, 1) * at(m, 11) - at(m, 9) * at(m, 3))) * invDet;
|
|
202
|
+
inv[10] = (at(m, 0) * (at(m, 5) * at(m, 15) - at(m, 13) * at(m, 7)) - at(m, 4) * (at(m, 1) * at(m, 15) - at(m, 13) * at(m, 3)) + at(m, 12) * (at(m, 1) * at(m, 7) - at(m, 5) * at(m, 3))) * invDet;
|
|
203
|
+
inv[11] = -(at(m, 0) * (at(m, 5) * at(m, 11) - at(m, 9) * at(m, 7)) - at(m, 4) * (at(m, 1) * at(m, 11) - at(m, 9) * at(m, 3)) + at(m, 8) * (at(m, 1) * at(m, 7) - at(m, 5) * at(m, 3))) * invDet;
|
|
204
|
+
inv[12] = -(at(m, 4) * (at(m, 9) * at(m, 14) - at(m, 13) * at(m, 10)) - at(m, 8) * (at(m, 5) * at(m, 14) - at(m, 13) * at(m, 6)) + at(m, 12) * (at(m, 5) * at(m, 10) - at(m, 9) * at(m, 6))) * invDet;
|
|
205
|
+
inv[13] = (at(m, 0) * (at(m, 9) * at(m, 14) - at(m, 13) * at(m, 10)) - at(m, 8) * (at(m, 1) * at(m, 14) - at(m, 13) * at(m, 2)) + at(m, 12) * (at(m, 1) * at(m, 10) - at(m, 9) * at(m, 2))) * invDet;
|
|
206
|
+
inv[14] = -(at(m, 0) * (at(m, 5) * at(m, 14) - at(m, 13) * at(m, 6)) - at(m, 4) * (at(m, 1) * at(m, 14) - at(m, 13) * at(m, 2)) + at(m, 12) * (at(m, 1) * at(m, 6) - at(m, 5) * at(m, 2))) * invDet;
|
|
207
|
+
inv[15] = (at(m, 0) * (at(m, 5) * at(m, 10) - at(m, 9) * at(m, 6)) - at(m, 4) * (at(m, 1) * at(m, 10) - at(m, 9) * at(m, 2)) + at(m, 8) * (at(m, 1) * at(m, 6) - at(m, 5) * at(m, 2))) * invDet;
|
|
208
|
+
const ndc = [
|
|
209
|
+
[-1, -1, 0, 1],
|
|
210
|
+
[1, -1, 0, 1],
|
|
211
|
+
[-1, 1, 0, 1],
|
|
212
|
+
[1, 1, 0, 1],
|
|
213
|
+
[-1, -1, 1, 1],
|
|
214
|
+
[1, -1, 1, 1],
|
|
215
|
+
[-1, 1, 1, 1],
|
|
216
|
+
[1, 1, 1, 1]
|
|
217
|
+
];
|
|
218
|
+
const corners = ndc.map(([nx, ny, nz, nw]) => {
|
|
219
|
+
const cx = at(inv, 0) * nx + at(inv, 4) * ny + at(inv, 8) * nz + at(inv, 12) * nw;
|
|
220
|
+
const cy = at(inv, 1) * nx + at(inv, 5) * ny + at(inv, 9) * nz + at(inv, 13) * nw;
|
|
221
|
+
const cz = at(inv, 2) * nx + at(inv, 6) * ny + at(inv, 10) * nz + at(inv, 14) * nw;
|
|
222
|
+
const cw = at(inv, 3) * nx + at(inv, 7) * ny + at(inv, 11) * nz + at(inv, 15) * nw;
|
|
223
|
+
const iw = 1 / cw;
|
|
224
|
+
return [cx * iw, cy * iw, cz * iw];
|
|
225
|
+
});
|
|
226
|
+
const edges = [
|
|
227
|
+
[0, 1],
|
|
228
|
+
[0, 2],
|
|
229
|
+
[1, 3],
|
|
230
|
+
[2, 3],
|
|
231
|
+
[4, 5],
|
|
232
|
+
[4, 6],
|
|
233
|
+
[5, 7],
|
|
234
|
+
[6, 7],
|
|
235
|
+
[0, 4],
|
|
236
|
+
[1, 5],
|
|
237
|
+
[2, 6],
|
|
238
|
+
[3, 7]
|
|
239
|
+
];
|
|
240
|
+
const result = [];
|
|
241
|
+
for (const [ai, bi] of edges) {
|
|
242
|
+
const ac = corners[ai];
|
|
243
|
+
const bc = corners[bi];
|
|
244
|
+
result.push([ac[0], ac[1], ac[2]]);
|
|
245
|
+
result.push([bc[0], bc[1], bc[2]]);
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/shapes/line.ts
|
|
251
|
+
function lineVertices(a3, b) {
|
|
252
|
+
return [
|
|
253
|
+
[a3[0], a3[1], a3[2]],
|
|
254
|
+
[b[0], b[1], b[2]]
|
|
255
|
+
];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/shapes/sphere.ts
|
|
259
|
+
function a2(v, i) {
|
|
260
|
+
return v[i];
|
|
261
|
+
}
|
|
262
|
+
function sphereVertices(center, radius, segments) {
|
|
263
|
+
const cx = a2(center, 0);
|
|
264
|
+
const cy = a2(center, 1);
|
|
265
|
+
const cz = a2(center, 2);
|
|
266
|
+
const step = 2 * Math.PI / segments;
|
|
267
|
+
const result = [];
|
|
268
|
+
for (let plane = 0; plane < 3; plane++) {
|
|
269
|
+
for (let i = 0; i < segments; i++) {
|
|
270
|
+
const angle0 = i * step;
|
|
271
|
+
const angle1 = (i + 1) % segments;
|
|
272
|
+
let p0x;
|
|
273
|
+
let p0y;
|
|
274
|
+
let p0z;
|
|
275
|
+
let p1x;
|
|
276
|
+
let p1y;
|
|
277
|
+
let p1z;
|
|
278
|
+
if (plane === 0) {
|
|
279
|
+
p0x = cx + radius * Math.cos(angle0);
|
|
280
|
+
p0y = cy + radius * Math.sin(angle0);
|
|
281
|
+
p0z = cz;
|
|
282
|
+
p1x = cx + radius * Math.cos(angle1);
|
|
283
|
+
p1y = cy + radius * Math.sin(angle1);
|
|
284
|
+
p1z = cz;
|
|
285
|
+
} else if (plane === 1) {
|
|
286
|
+
p0x = cx + radius * Math.cos(angle0);
|
|
287
|
+
p0y = cy;
|
|
288
|
+
p0z = cz + radius * Math.sin(angle0);
|
|
289
|
+
p1x = cx + radius * Math.cos(angle1);
|
|
290
|
+
p1y = cy;
|
|
291
|
+
p1z = cz + radius * Math.sin(angle1);
|
|
292
|
+
} else {
|
|
293
|
+
p0x = cx;
|
|
294
|
+
p0y = cy + radius * Math.cos(angle0);
|
|
295
|
+
p0z = cz + radius * Math.sin(angle0);
|
|
296
|
+
p1x = cx;
|
|
297
|
+
p1y = cy + radius * Math.cos(angle1);
|
|
298
|
+
p1z = cz + radius * Math.sin(angle1);
|
|
299
|
+
}
|
|
300
|
+
result.push([p0x, p0y, p0z]);
|
|
301
|
+
result.push([p1x, p1y, p1z]);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/debug-draw.ts
|
|
308
|
+
var VERTEX_SHADER = (
|
|
309
|
+
/* wgsl */
|
|
310
|
+
`
|
|
311
|
+
struct VertexInput {
|
|
312
|
+
@location(0) position: vec3<f32>,
|
|
313
|
+
@location(1) color: vec4<f32>,
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
struct VertexOutput {
|
|
317
|
+
@builtin(position) position: vec4<f32>,
|
|
318
|
+
@location(0) color: vec4<f32>,
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
struct Uniforms {
|
|
322
|
+
viewProj: mat4x4<f32>,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
326
|
+
|
|
327
|
+
@vertex
|
|
328
|
+
fn vs_main(in: VertexInput) -> VertexOutput {
|
|
329
|
+
var out: VertexOutput;
|
|
330
|
+
out.position = uniforms.viewProj * vec4<f32>(in.position, 1.0);
|
|
331
|
+
out.color = in.color;
|
|
332
|
+
return out;
|
|
333
|
+
}
|
|
334
|
+
`
|
|
335
|
+
);
|
|
336
|
+
var FRAGMENT_SHADER = (
|
|
337
|
+
/* wgsl */
|
|
338
|
+
`
|
|
339
|
+
struct FragmentInput {
|
|
340
|
+
@location(0) color: vec4<f32>,
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@fragment
|
|
344
|
+
fn fs_main(in: FragmentInput) -> @location(0) vec4<f32> {
|
|
345
|
+
return in.color;
|
|
346
|
+
}
|
|
347
|
+
`
|
|
348
|
+
);
|
|
349
|
+
function at2(a3, i) {
|
|
350
|
+
return a3[i];
|
|
351
|
+
}
|
|
352
|
+
function normalizeCapacity(value, fallback) {
|
|
353
|
+
const finiteValue = Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
354
|
+
return Math.max(1, finiteValue);
|
|
355
|
+
}
|
|
356
|
+
var DebugDraw = class {
|
|
357
|
+
stagingArr;
|
|
358
|
+
stagingLen = 0;
|
|
359
|
+
lastFlushedVertexCount = 0;
|
|
360
|
+
capVal;
|
|
361
|
+
gpuVbo = null;
|
|
362
|
+
gpuPipeline = null;
|
|
363
|
+
gpuUniformBuffer = null;
|
|
364
|
+
gpuBindGroup = null;
|
|
365
|
+
maxCapVal;
|
|
366
|
+
rhiDevice;
|
|
367
|
+
isDestroyed = false;
|
|
368
|
+
// Whether the destroy-after-shape warning has been emitted (plan-strategy D-11).
|
|
369
|
+
// private (no underscore, no @internal) — purely class-internal state, not part of
|
|
370
|
+
// package-internal API surface. Biome R-internal-A forbids `_x` on private fields;
|
|
371
|
+
// lint:internal R-internal-C requires `_x` for `@internal`. Drop both markers since
|
|
372
|
+
// there is no package-internal use for this field — accessing it from outside the
|
|
373
|
+
// class is meaningless.
|
|
374
|
+
destroyedWarnedOnce = false;
|
|
375
|
+
// Hard-cap diagnostics are per frame: flush() clears this flag with staging.
|
|
376
|
+
truncationWarned = false;
|
|
377
|
+
/**
|
|
378
|
+
* Depth texture view for less-equal depth mode.
|
|
379
|
+
* Set via {@link _setDepthView} before flush() when depthMode is 'less-equal'.
|
|
380
|
+
* The runtime auto-attach path receives depth from the render-graph context;
|
|
381
|
+
* low-path callers (test harnesses, smoke runners) set this explicitly.
|
|
382
|
+
*/
|
|
383
|
+
depthView = null;
|
|
384
|
+
constructor(device, pipeline, vbo, uniformBuffer, bindGroup, initialCapacity, maxCapacity) {
|
|
385
|
+
const boundedMaxCapacity = normalizeCapacity(maxCapacity, MAX_VERTEX_CAPACITY);
|
|
386
|
+
const boundedInitialCapacity = Math.min(
|
|
387
|
+
normalizeCapacity(initialCapacity, INITIAL_VERTEX_CAPACITY),
|
|
388
|
+
boundedMaxCapacity
|
|
389
|
+
);
|
|
390
|
+
this.rhiDevice = device;
|
|
391
|
+
this.gpuPipeline = pipeline;
|
|
392
|
+
this.gpuVbo = vbo;
|
|
393
|
+
this.gpuUniformBuffer = uniformBuffer;
|
|
394
|
+
this.gpuBindGroup = bindGroup;
|
|
395
|
+
this.capVal = boundedInitialCapacity;
|
|
396
|
+
this.maxCapVal = boundedMaxCapacity;
|
|
397
|
+
this.stagingArr = new Float32Array(boundedInitialCapacity * (VERTEX_STRIDE_BYTES / 4));
|
|
398
|
+
}
|
|
399
|
+
/** @internal CPU staging vertex count (exposed for unit tests). */
|
|
400
|
+
get _stagingVertexCount() {
|
|
401
|
+
return this.stagingLen;
|
|
402
|
+
}
|
|
403
|
+
/** @internal Vertex count passed to the most recent non-empty draw call. */
|
|
404
|
+
get _lastFlushVertexCount() {
|
|
405
|
+
return this.lastFlushedVertexCount;
|
|
406
|
+
}
|
|
407
|
+
/** @internal Current GPU vertex buffer capacity in vertex count. */
|
|
408
|
+
get _capacity() {
|
|
409
|
+
return this.capVal;
|
|
410
|
+
}
|
|
411
|
+
/** @internal Whether destroy() has been called. */
|
|
412
|
+
get _destroyed() {
|
|
413
|
+
return this.isDestroyed;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* @internal Set the depth texture view for less-equal depth mode.
|
|
417
|
+
* Required before flush() when depthMode is 'less-equal'.
|
|
418
|
+
* Used by test harnesses and smoke runners that don't have a scene
|
|
419
|
+
* depth buffer; the runtime auto-attach path receives depth from
|
|
420
|
+
* the render-graph context.
|
|
421
|
+
*/
|
|
422
|
+
_setDepthView(view) {
|
|
423
|
+
this.depthView = view;
|
|
424
|
+
}
|
|
425
|
+
/** @internal Read position of vertex at `index` in CPU staging (for unit tests). */
|
|
426
|
+
_getVertexPosition(index) {
|
|
427
|
+
const idx = index * 4;
|
|
428
|
+
return [
|
|
429
|
+
this.stagingArr[idx + 0],
|
|
430
|
+
this.stagingArr[idx + 1],
|
|
431
|
+
this.stagingArr[idx + 2]
|
|
432
|
+
];
|
|
433
|
+
}
|
|
434
|
+
/** @internal Read color of vertex at `index` as packed u32 (for unit tests). */
|
|
435
|
+
_getVertexPackedColor(index) {
|
|
436
|
+
const byteOff = index * VERTEX_STRIDE_BYTES + 12;
|
|
437
|
+
const bytes = new Uint8Array(this.stagingArr.buffer, byteOff, 4);
|
|
438
|
+
return bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
|
|
439
|
+
}
|
|
440
|
+
postDestroyWarnOnce() {
|
|
441
|
+
if (!this.destroyedWarnedOnce) {
|
|
442
|
+
this.destroyedWarnedOnce = true;
|
|
443
|
+
console.warn(
|
|
444
|
+
"[DebugDraw] Shape call after destroy() is a no-op. Create a new instance via createDebugDraw()."
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
pushVertex(px, py, pz, rc, gc, bc, ac) {
|
|
449
|
+
if (this.isDestroyed) {
|
|
450
|
+
this.postDestroyWarnOnce();
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (this.stagingLen >= this.maxCapVal) return;
|
|
454
|
+
const idx = this.stagingLen * 4;
|
|
455
|
+
this.stagingArr[idx + 0] = px;
|
|
456
|
+
this.stagingArr[idx + 1] = py;
|
|
457
|
+
this.stagingArr[idx + 2] = pz;
|
|
458
|
+
const u8r = Math.round(Math.max(0, Math.min(1, rc)) * 255);
|
|
459
|
+
const u8g = Math.round(Math.max(0, Math.min(1, gc)) * 255);
|
|
460
|
+
const u8bc = Math.round(Math.max(0, Math.min(1, bc)) * 255);
|
|
461
|
+
const u8a = Math.round(Math.max(0, Math.min(1, ac)) * 255);
|
|
462
|
+
const byteOff = this.stagingLen * VERTEX_STRIDE_BYTES + 12;
|
|
463
|
+
const colorView = new Uint8Array(this.stagingArr.buffer, byteOff, 4);
|
|
464
|
+
colorView[0] = u8r;
|
|
465
|
+
colorView[1] = u8g;
|
|
466
|
+
colorView[2] = u8bc;
|
|
467
|
+
colorView[3] = u8a;
|
|
468
|
+
this.stagingLen++;
|
|
469
|
+
}
|
|
470
|
+
warnTruncationOnce() {
|
|
471
|
+
if (this.truncationWarned) return;
|
|
472
|
+
this.truncationWarned = true;
|
|
473
|
+
console.warn(
|
|
474
|
+
`[DebugDraw] Vertex count would exceed MAX_VERTEX_CAPACITY=${this.maxCapVal}; vertices beyond the limit are discarded.`
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
ensureCapacity(needed) {
|
|
478
|
+
if (this.isDestroyed) return;
|
|
479
|
+
if (needed <= this.capVal) return;
|
|
480
|
+
if (needed > this.maxCapVal) {
|
|
481
|
+
this.warnTruncationOnce();
|
|
482
|
+
}
|
|
483
|
+
let newCap = this.capVal;
|
|
484
|
+
while (newCap < needed && newCap < this.maxCapVal) {
|
|
485
|
+
newCap = Math.min(newCap * 2, this.maxCapVal);
|
|
486
|
+
}
|
|
487
|
+
if (newCap > this.capVal) {
|
|
488
|
+
const newVbo = this.rhiDevice.createBuffer({
|
|
489
|
+
size: newCap * VERTEX_STRIDE_BYTES,
|
|
490
|
+
usage: 8 | 32,
|
|
491
|
+
// COPY_DST | VERTEX (mirrors createDebugDraw factory)
|
|
492
|
+
label: "debug-draw-vbo"
|
|
493
|
+
});
|
|
494
|
+
if (!newVbo.ok) {
|
|
495
|
+
console.warn(
|
|
496
|
+
`[DebugDraw] GPU vertex buffer grow to ${newCap} failed (${newVbo.error.code}); keeping ${this.capVal} -- excess vertices are truncated this frame.`
|
|
497
|
+
);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
console.warn(`[DebugDraw] Resizing vertex buffer from ${this.capVal} to ${newCap} vertices.`);
|
|
501
|
+
if (this.gpuVbo !== null) this.rhiDevice.destroyBuffer(this.gpuVbo);
|
|
502
|
+
this.gpuVbo = newVbo.value;
|
|
503
|
+
this.capVal = newCap;
|
|
504
|
+
const newStaging = new Float32Array(newCap * (VERTEX_STRIDE_BYTES / 4));
|
|
505
|
+
newStaging.set(this.stagingArr.subarray(0, this.stagingLen * 4));
|
|
506
|
+
this.stagingArr = newStaging;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
colorToRGBA(color) {
|
|
510
|
+
if (Array.isArray(color)) {
|
|
511
|
+
return [at2(color, 0), at2(color, 1), at2(color, 2), color[3] ?? 1];
|
|
512
|
+
}
|
|
513
|
+
return [at2(color, 0), at2(color, 1), at2(color, 2), color[3] ?? 1];
|
|
514
|
+
}
|
|
515
|
+
// -- Public shape API --
|
|
516
|
+
line(a3, b, color) {
|
|
517
|
+
if (this.isDestroyed) {
|
|
518
|
+
this.postDestroyWarnOnce();
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const [r, g, bc, alpha] = this.colorToRGBA(color);
|
|
522
|
+
this.ensureCapacity(this.stagingLen + 2);
|
|
523
|
+
for (const [x, y, z] of lineVertices(a3, b)) {
|
|
524
|
+
this.pushVertex(x, y, z, r, g, bc, alpha);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
aabb(min, max, color) {
|
|
528
|
+
if (this.isDestroyed) {
|
|
529
|
+
this.postDestroyWarnOnce();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const [r, g, bc, alpha] = this.colorToRGBA(color);
|
|
533
|
+
const verts = aabbVertices(min, max);
|
|
534
|
+
this.ensureCapacity(this.stagingLen + verts.length);
|
|
535
|
+
for (const [x, y, z] of verts) {
|
|
536
|
+
this.pushVertex(x, y, z, r, g, bc, alpha);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
sphere(center, radius, color, segments = 16) {
|
|
540
|
+
if (this.isDestroyed) {
|
|
541
|
+
this.postDestroyWarnOnce();
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const [r, g, bc, alpha] = this.colorToRGBA(color);
|
|
545
|
+
const verts = sphereVertices(center, radius, segments);
|
|
546
|
+
this.ensureCapacity(this.stagingLen + verts.length);
|
|
547
|
+
for (const [x, y, z] of verts) {
|
|
548
|
+
this.pushVertex(x, y, z, r, g, bc, alpha);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
frustum(viewProj, color) {
|
|
552
|
+
if (this.isDestroyed) {
|
|
553
|
+
this.postDestroyWarnOnce();
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const verts = frustumVertices(viewProj);
|
|
557
|
+
if (verts === null) {
|
|
558
|
+
console.warn(
|
|
559
|
+
"[DebugDraw] frustum() received a near-singular viewProj matrix; skipping this frame."
|
|
560
|
+
);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const [r, g, bc, alpha] = this.colorToRGBA(color);
|
|
564
|
+
this.ensureCapacity(this.stagingLen + verts.length);
|
|
565
|
+
for (const [x, y, z] of verts) {
|
|
566
|
+
this.pushVertex(x, y, z, r, g, bc, alpha);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
arrow(start, end, color, tipLength) {
|
|
570
|
+
if (this.isDestroyed) {
|
|
571
|
+
this.postDestroyWarnOnce();
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const [r, g, bc, alpha] = this.colorToRGBA(color);
|
|
575
|
+
const verts = arrowVertices(start, end, tipLength);
|
|
576
|
+
this.ensureCapacity(this.stagingLen + verts.length);
|
|
577
|
+
for (const [x, y, z] of verts) {
|
|
578
|
+
this.pushVertex(x, y, z, r, g, bc, alpha);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
axes(worldMat, length) {
|
|
582
|
+
if (this.isDestroyed) {
|
|
583
|
+
this.postDestroyWarnOnce();
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
for (const { vertices, color } of axesArrowSets(worldMat, length)) {
|
|
587
|
+
const [r, g, bc, alpha] = this.colorToRGBA(color);
|
|
588
|
+
this.ensureCapacity(this.stagingLen + vertices.length);
|
|
589
|
+
for (const [x, y, z] of vertices) {
|
|
590
|
+
this.pushVertex(x, y, z, r, g, bc, alpha);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
// -- flush (w13) --
|
|
595
|
+
flush(encoder, view, viewProj) {
|
|
596
|
+
if (this.isDestroyed) return flushedAfterDestroy();
|
|
597
|
+
if (viewProj === void 0 || viewProj === null) return viewProjRequired();
|
|
598
|
+
if (this.stagingLen === 0) {
|
|
599
|
+
this.lastFlushedVertexCount = 0;
|
|
600
|
+
return ok(void 0);
|
|
601
|
+
}
|
|
602
|
+
const passDesc = {
|
|
603
|
+
colorAttachments: [
|
|
604
|
+
{
|
|
605
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
606
|
+
view,
|
|
607
|
+
loadOp: "load",
|
|
608
|
+
storeOp: "store"
|
|
609
|
+
}
|
|
610
|
+
]
|
|
611
|
+
};
|
|
612
|
+
if (this.depthView !== null) {
|
|
613
|
+
passDesc.depthStencilAttachment = {
|
|
614
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque depth view
|
|
615
|
+
view: this.depthView,
|
|
616
|
+
depthLoadOp: "load",
|
|
617
|
+
depthStoreOp: "store"
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
const pass = encoder.beginRenderPass(passDesc);
|
|
621
|
+
const encoded = this.encode(pass, viewProj);
|
|
622
|
+
pass.end();
|
|
623
|
+
return encoded;
|
|
624
|
+
}
|
|
625
|
+
encode(pass, viewProj) {
|
|
626
|
+
if (this.isDestroyed) return flushedAfterDestroy();
|
|
627
|
+
if (viewProj === void 0 || viewProj === null) return viewProjRequired();
|
|
628
|
+
if (this.stagingLen === 0) {
|
|
629
|
+
this.lastFlushedVertexCount = 0;
|
|
630
|
+
return ok(void 0);
|
|
631
|
+
}
|
|
632
|
+
const vertexCount = Math.min(this.stagingLen, this.maxCapVal);
|
|
633
|
+
const vbo = this.gpuVbo;
|
|
634
|
+
const pipeline = this.gpuPipeline;
|
|
635
|
+
const uniformBuf = this.gpuUniformBuffer;
|
|
636
|
+
const bindGroup = this.gpuBindGroup;
|
|
637
|
+
const byteCount = vertexCount * VERTEX_STRIDE_BYTES;
|
|
638
|
+
this.rhiDevice.queue.writeBuffer(
|
|
639
|
+
vbo,
|
|
640
|
+
0,
|
|
641
|
+
new Uint8Array(this.stagingArr.buffer, 0, byteCount),
|
|
642
|
+
0,
|
|
643
|
+
byteCount
|
|
644
|
+
);
|
|
645
|
+
const uniformData = new Float32Array(16);
|
|
646
|
+
for (let i = 0; i < 16; i++) uniformData[i] = viewProj[i];
|
|
647
|
+
this.rhiDevice.queue.writeBuffer(uniformBuf, 0, new Uint8Array(uniformData.buffer), 0, 64);
|
|
648
|
+
pass.setPipeline(pipeline);
|
|
649
|
+
pass.setBindGroup(0, bindGroup);
|
|
650
|
+
pass.setVertexBuffer(0, vbo);
|
|
651
|
+
pass.draw(vertexCount);
|
|
652
|
+
this.lastFlushedVertexCount = vertexCount;
|
|
653
|
+
this.stagingLen = 0;
|
|
654
|
+
this.truncationWarned = false;
|
|
655
|
+
return ok(void 0);
|
|
656
|
+
}
|
|
657
|
+
// -- destroy (w14) --
|
|
658
|
+
destroy() {
|
|
659
|
+
if (this.isDestroyed) return;
|
|
660
|
+
this.isDestroyed = true;
|
|
661
|
+
if (this.gpuVbo) {
|
|
662
|
+
this.rhiDevice.destroyBuffer(this.gpuVbo);
|
|
663
|
+
this.gpuVbo = null;
|
|
664
|
+
}
|
|
665
|
+
if (this.gpuUniformBuffer) {
|
|
666
|
+
this.rhiDevice.destroyBuffer(this.gpuUniformBuffer);
|
|
667
|
+
this.gpuUniformBuffer = null;
|
|
668
|
+
}
|
|
669
|
+
this.gpuBindGroup = null;
|
|
670
|
+
this.gpuPipeline = null;
|
|
671
|
+
this.stagingArr = new Float32Array(0);
|
|
672
|
+
this.stagingLen = 0;
|
|
673
|
+
this.lastFlushedVertexCount = 0;
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
async function createDebugDraw(opts) {
|
|
677
|
+
const device = opts.device;
|
|
678
|
+
const fmt = opts.format ?? "bgra8unorm";
|
|
679
|
+
const depthFormat = opts.depthFormat;
|
|
680
|
+
const depthMode = opts.depthMode ?? "always";
|
|
681
|
+
const maxCap = normalizeCapacity(
|
|
682
|
+
opts.maxVertexCapacity ?? MAX_VERTEX_CAPACITY,
|
|
683
|
+
MAX_VERTEX_CAPACITY
|
|
684
|
+
);
|
|
685
|
+
const initialCap = Math.min(
|
|
686
|
+
normalizeCapacity(
|
|
687
|
+
opts.initialVertexCapacity ?? INITIAL_VERTEX_CAPACITY,
|
|
688
|
+
INITIAL_VERTEX_CAPACITY
|
|
689
|
+
),
|
|
690
|
+
maxCap
|
|
691
|
+
);
|
|
692
|
+
const vboByteSize = initialCap * VERTEX_STRIDE_BYTES;
|
|
693
|
+
const vboResult = device.createBuffer({
|
|
694
|
+
size: vboByteSize,
|
|
695
|
+
usage: 8 | 32,
|
|
696
|
+
// COPY_DST | VERTEX
|
|
697
|
+
label: "debug-draw-vbo"
|
|
698
|
+
});
|
|
699
|
+
if (!vboResult.ok) {
|
|
700
|
+
return bufferAllocationFailed(
|
|
701
|
+
`createBuffer(COPY_DST|VERTEX, ${vboByteSize}B): ${vboResult.error.code}`
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
const vbo = vboResult.value;
|
|
705
|
+
const uniformBufResult = device.createBuffer({
|
|
706
|
+
size: 64,
|
|
707
|
+
usage: 64 | 8,
|
|
708
|
+
// UNIFORM | COPY_DST
|
|
709
|
+
label: "debug-draw-uniform"
|
|
710
|
+
});
|
|
711
|
+
if (!uniformBufResult.ok) {
|
|
712
|
+
device.destroyBuffer(vbo);
|
|
713
|
+
return bufferAllocationFailed(
|
|
714
|
+
`createBuffer(UNIFORM|COPY_DST, 64B): ${uniformBufResult.error.code}`
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
const uniformBuf = uniformBufResult.value;
|
|
718
|
+
const vsResult = await opts.createShaderModule(device, {
|
|
719
|
+
label: "debug-draw-vs",
|
|
720
|
+
code: VERTEX_SHADER
|
|
721
|
+
});
|
|
722
|
+
if (!vsResult.ok) {
|
|
723
|
+
device.destroyBuffer(vbo);
|
|
724
|
+
return pipelineCreateFailed(`createShaderModule(vertex): ${vsResult.error.code}`);
|
|
725
|
+
}
|
|
726
|
+
const vsModule = vsResult.value;
|
|
727
|
+
const fsResult = await opts.createShaderModule(device, {
|
|
728
|
+
label: "debug-draw-fs",
|
|
729
|
+
code: FRAGMENT_SHADER
|
|
730
|
+
});
|
|
731
|
+
if (!fsResult.ok) {
|
|
732
|
+
device.destroyBuffer(vbo);
|
|
733
|
+
return pipelineCreateFailed(`createShaderModule(fragment): ${fsResult.error.code}`);
|
|
734
|
+
}
|
|
735
|
+
const fsModule = fsResult.value;
|
|
736
|
+
const bglResult = device.createBindGroupLayout({
|
|
737
|
+
label: "debug-draw-bind-group-layout",
|
|
738
|
+
entries: [
|
|
739
|
+
{
|
|
740
|
+
binding: 0,
|
|
741
|
+
visibility: 1,
|
|
742
|
+
buffer: { type: "uniform", minBindingSize: 64 }
|
|
743
|
+
}
|
|
744
|
+
]
|
|
745
|
+
});
|
|
746
|
+
if (!bglResult.ok) {
|
|
747
|
+
device.destroyBuffer(vbo);
|
|
748
|
+
device.destroyBuffer(uniformBuf);
|
|
749
|
+
return pipelineCreateFailed(`createBindGroupLayout: ${bglResult.error.code}`);
|
|
750
|
+
}
|
|
751
|
+
const pipelineLayoutResult = device.createPipelineLayout({
|
|
752
|
+
label: "debug-draw-pipeline-layout",
|
|
753
|
+
bindGroupLayouts: [bglResult.value]
|
|
754
|
+
});
|
|
755
|
+
if (!pipelineLayoutResult.ok) {
|
|
756
|
+
device.destroyBuffer(vbo);
|
|
757
|
+
device.destroyBuffer(uniformBuf);
|
|
758
|
+
return pipelineCreateFailed(`createPipelineLayout: ${pipelineLayoutResult.error.code}`);
|
|
759
|
+
}
|
|
760
|
+
const depthStencil = depthMode === "less-equal" ? {
|
|
761
|
+
format: depthFormat ?? "depth24plus",
|
|
762
|
+
depthWriteEnabled: false,
|
|
763
|
+
depthCompare: "less-equal"
|
|
764
|
+
} : void 0;
|
|
765
|
+
const vertexBuffers = [
|
|
766
|
+
{
|
|
767
|
+
arrayStride: VERTEX_STRIDE_BYTES,
|
|
768
|
+
stepMode: "vertex",
|
|
769
|
+
attributes: [
|
|
770
|
+
{
|
|
771
|
+
format: "float32x3",
|
|
772
|
+
offset: 0,
|
|
773
|
+
shaderLocation: 0
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
format: "unorm8x4",
|
|
777
|
+
offset: 12,
|
|
778
|
+
shaderLocation: 1
|
|
779
|
+
}
|
|
780
|
+
]
|
|
781
|
+
}
|
|
782
|
+
];
|
|
783
|
+
const pipelineDesc = {
|
|
784
|
+
label: "debug-draw-pso",
|
|
785
|
+
layout: pipelineLayoutResult.value,
|
|
786
|
+
vertex: {
|
|
787
|
+
module: vsModule,
|
|
788
|
+
entryPoint: "vs_main",
|
|
789
|
+
buffers: [...vertexBuffers]
|
|
790
|
+
},
|
|
791
|
+
primitive: {
|
|
792
|
+
topology: "line-list"
|
|
793
|
+
},
|
|
794
|
+
depthStencil,
|
|
795
|
+
fragment: {
|
|
796
|
+
module: fsModule,
|
|
797
|
+
entryPoint: "fs_main",
|
|
798
|
+
targets: [{ format: fmt }]
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
const psoResult = device.createRenderPipeline(pipelineDesc);
|
|
802
|
+
if (!psoResult.ok) {
|
|
803
|
+
device.destroyBuffer(vbo);
|
|
804
|
+
device.destroyBuffer(uniformBuf);
|
|
805
|
+
return pipelineCreateFailed(`createRenderPipeline: ${psoResult.error.code}`);
|
|
806
|
+
}
|
|
807
|
+
const pipeline = psoResult.value;
|
|
808
|
+
const bgResult = device.createBindGroup({
|
|
809
|
+
layout: bglResult.value,
|
|
810
|
+
entries: [
|
|
811
|
+
{
|
|
812
|
+
binding: 0,
|
|
813
|
+
resource: {
|
|
814
|
+
kind: "buffer",
|
|
815
|
+
value: { buffer: uniformBuf, offset: 0, size: 64 }
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
],
|
|
819
|
+
label: "debug-draw-bindgroup"
|
|
820
|
+
// biome-ignore lint/suspicious/noExplicitAny: forgeax opaque BGL -> createBindGroup descriptor
|
|
821
|
+
});
|
|
822
|
+
if (!bgResult.ok) {
|
|
823
|
+
device.destroyBuffer(vbo);
|
|
824
|
+
device.destroyBuffer(uniformBuf);
|
|
825
|
+
return pipelineCreateFailed(`createBindGroup: ${bgResult.error.code}`);
|
|
826
|
+
}
|
|
827
|
+
return ok(new DebugDraw(device, pipeline, vbo, uniformBuf, bgResult.value, initialCap, maxCap));
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/render-feature.ts
|
|
831
|
+
function createDebugDrawRenderFeaturePlan(input) {
|
|
832
|
+
const program = "debug-draw.program";
|
|
833
|
+
const bindings = "debug-draw.bindings";
|
|
834
|
+
const vertices = "debug-draw.vertices";
|
|
835
|
+
const vertexData = "debug-draw.vertex-data";
|
|
836
|
+
return {
|
|
837
|
+
resources: [
|
|
838
|
+
{
|
|
839
|
+
kind: "graphics-program",
|
|
840
|
+
name: program,
|
|
841
|
+
program: {
|
|
842
|
+
shader: "forgeax::debug-draw.line",
|
|
843
|
+
vertexLayout: "debug-draw-line",
|
|
844
|
+
colorFormats: [input.colorFormat ?? "bgra8unorm"],
|
|
845
|
+
topology: "line-list"
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
kind: "graphics-bindings",
|
|
850
|
+
name: bindings,
|
|
851
|
+
program,
|
|
852
|
+
values: {
|
|
853
|
+
viewProjection: input.viewProjection ?? "debug-draw.view-projection"
|
|
854
|
+
}
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
kind: "buffer",
|
|
858
|
+
name: vertices,
|
|
859
|
+
size: Math.max(1, input.vertexCapacity) * 16,
|
|
860
|
+
usage: ["vertex"]
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
kind: "vertex-data",
|
|
864
|
+
name: vertexData,
|
|
865
|
+
layout: "debug-draw-line",
|
|
866
|
+
buffer: vertices
|
|
867
|
+
}
|
|
868
|
+
],
|
|
869
|
+
passes: [
|
|
870
|
+
{
|
|
871
|
+
kind: "raster",
|
|
872
|
+
name: "debug-draw.raster",
|
|
873
|
+
colorAttachments: [{ target: input.target, loadOp: "load", storeOp: "store" }],
|
|
874
|
+
draws: [
|
|
875
|
+
{
|
|
876
|
+
program,
|
|
877
|
+
bindings: [bindings],
|
|
878
|
+
vertexData: [{ slot: 0, resource: vertexData }],
|
|
879
|
+
draw: {
|
|
880
|
+
kind: "draw",
|
|
881
|
+
vertexCount: Math.max(0, input.vertexCount ?? input.vertexCapacity),
|
|
882
|
+
instanceCount: 1
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
]
|
|
886
|
+
}
|
|
887
|
+
]
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
export { DebugDraw, INITIAL_VERTEX_CAPACITY, MAX_VERTEX_CAPACITY, VERTEX_STRIDE_BYTES, createDebugDraw, createDebugDrawRenderFeaturePlan };
|
|
892
|
+
//# sourceMappingURL=index.mjs.map
|
|
893
|
+
//# sourceMappingURL=index.mjs.map
|