phoria2 1.2.0 → 1.4.0
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/README.md +11 -9
- package/dist/bloom.js +4 -4
- package/dist/bloom.js.map +1 -1
- package/dist/charts.js +527 -510
- package/dist/charts.js.map +1 -1
- package/dist/core.js +1760 -1170
- package/dist/core.js.map +1 -1
- package/dist/debug.js +297 -0
- package/dist/debug.js.map +1 -0
- package/dist/edit.js +60 -60
- package/dist/edit.js.map +1 -1
- package/dist/explain.js +32 -13
- package/dist/explain.js.map +1 -1
- package/dist/gltf.js +155 -155
- package/dist/gltf.js.map +1 -1
- package/dist/keyboard.js +188 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/motion.js +35 -35
- package/dist/motion.js.map +1 -1
- package/dist/particles.js +187 -114
- package/dist/particles.js.map +1 -1
- package/dist/passes.js +116 -104
- package/dist/passes.js.map +1 -1
- package/dist/phoria2.js +1 -1
- package/dist/picking.js +95 -62
- package/dist/picking.js.map +1 -1
- package/dist/snapshot.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/types/View.d.ts +27 -0
- package/dist/types/area.d.ts +18 -0
- package/dist/types/debug.d.ts +74 -0
- package/dist/types/explain.d.ts +4 -0
- package/dist/types/frame.d.ts +5 -0
- package/dist/types/geometry/Geometry.d.ts +16 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/material/Material.d.ts +67 -0
- package/dist/types/material/Texture.d.ts +18 -2
- package/dist/types/particles/Emitter.d.ts +39 -2
- package/dist/types/scene/drawables.d.ts +23 -2
- package/dist/types/watch.d.ts +8 -1
- package/dist/types/webgl/hooks.d.ts +48 -0
- package/dist/types/webgl/passes.d.ts +4 -0
- package/dist/types/webgl/programs.d.ts +15 -5
- package/dist/types/webgl/resources.d.ts +12 -2
- package/dist/types/webgl/shaders.d.ts +41 -5
- package/llms-full.txt +275 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/schema/scene.schema.json +75 -0
- package/src/View.js +121 -7
- package/src/area.js +131 -0
- package/src/charts/marks.js +29 -2
- package/src/debug.js +283 -0
- package/src/explain.js +19 -0
- package/src/frame.js +7 -2
- package/src/geometry/Geometry.js +16 -1
- package/src/index.js +1 -0
- package/src/material/Material.js +130 -4
- package/src/material/Texture.js +55 -2
- package/src/particles/Emitter.js +121 -10
- package/src/scene/drawables.js +89 -8
- package/src/watch.js +26 -3
- package/src/webgl/WebGLRenderer.js +55 -7
- package/src/webgl/hooks.js +121 -0
- package/src/webgl/passes.js +17 -2
- package/src/webgl/picking.js +45 -3
- package/src/webgl/programs.js +20 -9
- package/src/webgl/resources.js +169 -15
- package/src/webgl/shaders.js +545 -38
package/dist/debug.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { $ as e, B as t, Dt as n, Et as r, Ot as i, pt as a, rt as o } from "./core.js";
|
|
2
|
+
// src/debug.js
|
|
3
|
+
var s = {
|
|
4
|
+
shadow: [
|
|
5
|
+
1,
|
|
6
|
+
.8,
|
|
7
|
+
0
|
|
8
|
+
],
|
|
9
|
+
bounds: [
|
|
10
|
+
0,
|
|
11
|
+
.9,
|
|
12
|
+
1
|
|
13
|
+
],
|
|
14
|
+
normal: [
|
|
15
|
+
1,
|
|
16
|
+
.31,
|
|
17
|
+
.85
|
|
18
|
+
],
|
|
19
|
+
camera: [
|
|
20
|
+
1,
|
|
21
|
+
1,
|
|
22
|
+
1
|
|
23
|
+
]
|
|
24
|
+
}, c = 5e3, l = 4e4, u = class {
|
|
25
|
+
#e = new t();
|
|
26
|
+
#t = new a({ positions: [
|
|
27
|
+
0,
|
|
28
|
+
0,
|
|
29
|
+
0,
|
|
30
|
+
0,
|
|
31
|
+
0,
|
|
32
|
+
0
|
|
33
|
+
] });
|
|
34
|
+
#n = this.#e.add(new o({
|
|
35
|
+
geometry: this.#t,
|
|
36
|
+
pickable: !1,
|
|
37
|
+
frustumCulled: !1,
|
|
38
|
+
material: {
|
|
39
|
+
vertexColors: !0,
|
|
40
|
+
width: 1.5,
|
|
41
|
+
fog: !1
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
#r = this.#e.add(new o({
|
|
45
|
+
geometry: this.#t,
|
|
46
|
+
pickable: !1,
|
|
47
|
+
frustumCulled: !1,
|
|
48
|
+
renderOrder: 1e9,
|
|
49
|
+
material: {
|
|
50
|
+
vertexColors: !0,
|
|
51
|
+
width: 1,
|
|
52
|
+
opacity: .22,
|
|
53
|
+
depthTest: !1,
|
|
54
|
+
depthWrite: !1,
|
|
55
|
+
fog: !1
|
|
56
|
+
}
|
|
57
|
+
}));
|
|
58
|
+
#i = null;
|
|
59
|
+
#a = 0;
|
|
60
|
+
#o = [];
|
|
61
|
+
#s = [];
|
|
62
|
+
constructor(e) {
|
|
63
|
+
this.overlay = e;
|
|
64
|
+
}
|
|
65
|
+
add(t, n, r) {
|
|
66
|
+
this.#o = [], this.#s = [];
|
|
67
|
+
let o = [...t.opaque, ...t.transparent], c = r.bounds.isEmpty() ? 1 : Math.max(r.bounds.getRadius(), .001), l = r.bounds.isEmpty() ? new i() : r.bounds.getCenter();
|
|
68
|
+
n.lights && this.#p(t, c, l), n.shadows && this.#m(t), n.bounds && this.#h(o, n.bounds === "hovered" ? r.hovered : null, n.bounds === "hovered"), n.normals && this.#g(o, n.normals === "hovered" ? r.hovered : null, n.normals === "hovered", c * .025), n.axes && (this.#c([
|
|
69
|
+
0,
|
|
70
|
+
0,
|
|
71
|
+
0
|
|
72
|
+
], [
|
|
73
|
+
c * .5,
|
|
74
|
+
0,
|
|
75
|
+
0
|
|
76
|
+
], [
|
|
77
|
+
1,
|
|
78
|
+
.2,
|
|
79
|
+
.2
|
|
80
|
+
]), this.#c([
|
|
81
|
+
0,
|
|
82
|
+
0,
|
|
83
|
+
0
|
|
84
|
+
], [
|
|
85
|
+
0,
|
|
86
|
+
c * .5,
|
|
87
|
+
0
|
|
88
|
+
], [
|
|
89
|
+
.3,
|
|
90
|
+
1,
|
|
91
|
+
.3
|
|
92
|
+
]), this.#c([
|
|
93
|
+
0,
|
|
94
|
+
0,
|
|
95
|
+
0
|
|
96
|
+
], [
|
|
97
|
+
0,
|
|
98
|
+
0,
|
|
99
|
+
c * .5
|
|
100
|
+
], [
|
|
101
|
+
.3,
|
|
102
|
+
.5,
|
|
103
|
+
1
|
|
104
|
+
]));
|
|
105
|
+
let u = r.camera.target;
|
|
106
|
+
if (n.camera && u && this.#l(u, c * .04, s.camera), !this.#o.length) return {
|
|
107
|
+
opaque: 0,
|
|
108
|
+
transparent: 0
|
|
109
|
+
};
|
|
110
|
+
let d = this.#o.length, f = this.#t.colors;
|
|
111
|
+
this.#t.positions.length === d && f ? (this.#t.positions.set(this.#o), f.set(this.#s), this.#t.needsUpdate()) : (this.#t.dispose(), this.#t = new a({
|
|
112
|
+
positions: this.#o,
|
|
113
|
+
colors: this.#s
|
|
114
|
+
}), this.#n.geometry = this.#r.geometry = this.#t), this.#e.updateWorldTree();
|
|
115
|
+
let p = e(this.#e, r.camera);
|
|
116
|
+
return t.opaque.push(...p.opaque), t.transparent.push(...p.transparent), {
|
|
117
|
+
opaque: p.opaque.length,
|
|
118
|
+
transparent: p.transparent.length
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
stats(e, t, n, r) {
|
|
122
|
+
if (!e || !this.overlay || typeof document > "u") {
|
|
123
|
+
this.#i?.remove(), this.#i = null;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
let i = e === !0 ? "top-left" : e;
|
|
127
|
+
this.#i || (this.#i = document.createElement("div"), this.#i.className = "phoria-debug-stats", Object.assign(this.#i.style, {
|
|
128
|
+
position: "absolute",
|
|
129
|
+
zIndex: "3",
|
|
130
|
+
pointerEvents: "none",
|
|
131
|
+
padding: "6px 8px",
|
|
132
|
+
borderRadius: "6px",
|
|
133
|
+
background: "rgba(10, 12, 16, 0.72)",
|
|
134
|
+
color: "#e8ecf2",
|
|
135
|
+
font: "11px/1.45 ui-monospace, SFMono-Regular, Consolas, monospace",
|
|
136
|
+
whiteSpace: "pre"
|
|
137
|
+
}), this.overlay.append(this.#i));
|
|
138
|
+
let [a, o] = i.split("-");
|
|
139
|
+
Object.assign(this.#i.style, {
|
|
140
|
+
top: a === "top" ? "8px" : "",
|
|
141
|
+
bottom: a === "bottom" ? "8px" : "",
|
|
142
|
+
left: o === "left" ? "8px" : "",
|
|
143
|
+
right: o === "right" ? "8px" : ""
|
|
144
|
+
});
|
|
145
|
+
let s = performance.now();
|
|
146
|
+
if (s - this.#a < 250) return;
|
|
147
|
+
this.#a = s;
|
|
148
|
+
let c = (e) => e >= 1e6 ? `${(e / 1e6).toFixed(1)}M` : e >= 1e4 ? `${Math.round(e / 1e3)}k` : String(e), l = n > 0 ? Math.round(1e3 / n) : 0;
|
|
149
|
+
this.#i.textContent = [
|
|
150
|
+
`${l ? `${l} fps ${n.toFixed(1)} ms` : "idle"} cpu ${r.toFixed(1)} ms`,
|
|
151
|
+
`${t.drawCalls} draws ${c(t.triangles)} triangles`,
|
|
152
|
+
`${t.programs} programs${t.compiling ? ` (${t.compiling} compiling)` : ""} ${t.textures} textures ${t.geometries} geometries`
|
|
153
|
+
].join("\n");
|
|
154
|
+
}
|
|
155
|
+
dispose() {
|
|
156
|
+
this.#i?.remove(), this.#i = null, this.#t.dispose();
|
|
157
|
+
}
|
|
158
|
+
#c(e, t, n) {
|
|
159
|
+
this.#o.push(e[0], e[1], e[2], t[0], t[1], t[2]), this.#s.push(n[0], n[1], n[2], n[0], n[1], n[2]);
|
|
160
|
+
}
|
|
161
|
+
#l(e, t, n) {
|
|
162
|
+
for (let r = 0; r < 3; r++) {
|
|
163
|
+
let i = [
|
|
164
|
+
e[0],
|
|
165
|
+
e[1],
|
|
166
|
+
e[2]
|
|
167
|
+
], a = [
|
|
168
|
+
e[0],
|
|
169
|
+
e[1],
|
|
170
|
+
e[2]
|
|
171
|
+
];
|
|
172
|
+
i[r] -= t, a[r] += t, this.#c(i, a, n);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
#u(e, t, n, r, i, a = 32) {
|
|
176
|
+
let o = (i) => {
|
|
177
|
+
let o = i / a * Math.PI * 2, s = Math.cos(o) * r, c = Math.sin(o) * r;
|
|
178
|
+
return [
|
|
179
|
+
e[0] + t[0] * s + n[0] * c,
|
|
180
|
+
e[1] + t[1] * s + n[1] * c,
|
|
181
|
+
e[2] + t[2] * s + n[2] * c
|
|
182
|
+
];
|
|
183
|
+
};
|
|
184
|
+
for (let e = 0; e < a; e++) this.#c(o(e), o(e + 1), i);
|
|
185
|
+
}
|
|
186
|
+
#d(e, t) {
|
|
187
|
+
let { min: n, max: r } = e, i = (e) => [
|
|
188
|
+
e & 1 ? r[0] : n[0],
|
|
189
|
+
e & 2 ? r[1] : n[1],
|
|
190
|
+
e & 4 ? r[2] : n[2]
|
|
191
|
+
];
|
|
192
|
+
for (let [e, n] of m) this.#c(i(e), i(n), t);
|
|
193
|
+
}
|
|
194
|
+
#f(e, t) {
|
|
195
|
+
let r = new n().copy(e).invert(), a = (e) => new i(e & 1 ? 1 : -1, e & 2 ? 1 : -1, e & 4 ? 1 : -1).applyMatrix4(r);
|
|
196
|
+
for (let [e, n] of m) this.#c(a(e), a(n), t);
|
|
197
|
+
}
|
|
198
|
+
#p(e, t, n) {
|
|
199
|
+
for (let r of e.lights) {
|
|
200
|
+
let e = r.color.toSRGB().map((e) => Math.min(1, e));
|
|
201
|
+
if (r.kind === "directional" && r.direction) {
|
|
202
|
+
let a = new i().copy(r.direction).normalize(), o = g(n, a, -t * 1.1), s = g(n, a, -t * .45);
|
|
203
|
+
this.#c(o, s, e);
|
|
204
|
+
let [c, l] = h(a);
|
|
205
|
+
for (let n of [
|
|
206
|
+
c,
|
|
207
|
+
l,
|
|
208
|
+
c.clone().negate(),
|
|
209
|
+
l.clone().negate()
|
|
210
|
+
]) this.#c(s, g(g(s, a, -t * .08), n, t * .04), e);
|
|
211
|
+
} else if (r.kind === "point" && r.position) {
|
|
212
|
+
let n = new i().copy(r.position), a = t * .06;
|
|
213
|
+
this.#u(n, d, f, a, e, 16), this.#u(n, d, p, a, e, 16), this.#u(n, f, p, a, e, 16), r.range && this.#u(n, d, p, r.range, e, 48);
|
|
214
|
+
} else if (r.kind === "spot" && r.position && r.direction) {
|
|
215
|
+
let n = new i().copy(r.position), a = new i().copy(r.direction).normalize(), o = r.range ? Math.min(r.range, t * 2) : t, s = o * Math.tan(r.angle ?? Math.PI / 4), c = g(n, a, o), [l, u] = h(a);
|
|
216
|
+
this.#u(c, l, u, s, e, 32);
|
|
217
|
+
for (let t of [
|
|
218
|
+
l,
|
|
219
|
+
u,
|
|
220
|
+
l.clone().negate(),
|
|
221
|
+
u.clone().negate()
|
|
222
|
+
]) this.#c(n, g(c, t, s), e);
|
|
223
|
+
this.#c(n, c, e);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
#m(e) {
|
|
228
|
+
e.shadow && this.#f(e.shadow.viewProjection, s.shadow);
|
|
229
|
+
for (let t of e.spotShadows) this.#f(t.viewProjection, s.shadow);
|
|
230
|
+
}
|
|
231
|
+
#h(e, t, n) {
|
|
232
|
+
if (n) {
|
|
233
|
+
if (!t) return;
|
|
234
|
+
let e = t.node;
|
|
235
|
+
if (t.instance !== null && typeof e.eachInstanceBox == "function") {
|
|
236
|
+
let n = 0;
|
|
237
|
+
e.eachInstanceBox((e, i, a, o, c, l) => {
|
|
238
|
+
n++ === t.instance && this.#d(new r([
|
|
239
|
+
e,
|
|
240
|
+
i,
|
|
241
|
+
a
|
|
242
|
+
], [
|
|
243
|
+
o,
|
|
244
|
+
c,
|
|
245
|
+
l
|
|
246
|
+
]), s.bounds);
|
|
247
|
+
});
|
|
248
|
+
} else this.#d(e.expandBounds(new r()), s.bounds);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
let i = /* @__PURE__ */ new Set();
|
|
252
|
+
for (let t of e) {
|
|
253
|
+
if (i.has(t.node)) continue;
|
|
254
|
+
i.add(t.node);
|
|
255
|
+
let e = t.node.expandBounds(new r());
|
|
256
|
+
e.isEmpty() || this.#d(e, s.bounds);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
#g(e, t, n, r) {
|
|
260
|
+
let a = n ? t && t.node.drawMode === "triangles" ? [t.node] : [] : [...new Set(e.map((e) => e.node))].filter((e) => e.drawMode === "triangles"), o = l;
|
|
261
|
+
for (let e of a) {
|
|
262
|
+
if (e.instanceMatrices) continue;
|
|
263
|
+
let t = e.geometry, a = t.vertexCount;
|
|
264
|
+
if (!t.normals || !n && (a > c || a > o)) continue;
|
|
265
|
+
o -= a;
|
|
266
|
+
let l = e.worldMatrix, u = t.positions, d = t.normals, f = new i(), p = new i();
|
|
267
|
+
for (let e = 0; e < a; e++) f.set(u[e * 3], u[e * 3 + 1], u[e * 3 + 2]).applyMatrix4(l), p.set(l[0] * d[e * 3] + l[4] * d[e * 3 + 1] + l[8] * d[e * 3 + 2], l[1] * d[e * 3] + l[5] * d[e * 3 + 1] + l[9] * d[e * 3 + 2], l[2] * d[e * 3] + l[6] * d[e * 3 + 1] + l[10] * d[e * 3 + 2]).normalize(), this.#c(f, [
|
|
268
|
+
f[0] + p[0] * r,
|
|
269
|
+
f[1] + p[1] * r,
|
|
270
|
+
f[2] + p[2] * r
|
|
271
|
+
], s.normal);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}, d = new i(1, 0, 0), f = new i(0, 1, 0), p = new i(0, 0, 1), m = [
|
|
275
|
+
[0, 1],
|
|
276
|
+
[2, 3],
|
|
277
|
+
[4, 5],
|
|
278
|
+
[6, 7],
|
|
279
|
+
[0, 2],
|
|
280
|
+
[1, 3],
|
|
281
|
+
[4, 6],
|
|
282
|
+
[5, 7],
|
|
283
|
+
[0, 4],
|
|
284
|
+
[1, 5],
|
|
285
|
+
[2, 6],
|
|
286
|
+
[3, 7]
|
|
287
|
+
];
|
|
288
|
+
function h(e) {
|
|
289
|
+
let t = Math.abs(e[1]) < .9 ? f : d, n = e.clone().cross(t).normalize();
|
|
290
|
+
return [n, e.clone().cross(n).normalize()];
|
|
291
|
+
}
|
|
292
|
+
function g(e, t, n) {
|
|
293
|
+
return new i(e[0] + t[0] * n, e[1] + t[1] * n, e[2] + t[2] * n);
|
|
294
|
+
}
|
|
295
|
+
export { u as DebugDraw };
|
|
296
|
+
|
|
297
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug.js","names":["collect","Scene","Mat4","Box3","Vec3","Geometry","Lines","COLORS","NORMALS_PER_MESH","NORMALS_IN_ALL","DebugDraw","#scene","#geometry","overlay","#positions","#colors","items","frame","size","at","middle","o","#lights","#shadows","#bounds","#normals","#segment","target","#cross","n","colors","#solid","#faint","drawn","where","#stats","corner","v","h","now","#statsAt","k","x","fps","frameMs","cpuMs","info","a","b","c","p","r","segments","y","centre","u","i","box","j","EDGES","inverse","viewProjection","l","d","from","moved","to","across","s","#circle","X","Y","Z","length","end","#frustum","onlyHovered","hovered","node","#box","x0","y0","z0","x1","y1","z1","seen","item","meshes","budget","g","count","m","up"],"sources":["../src/debug.js"],"sourcesContent":["// view.debug: pictures for a person checking a scene - each light and the way it shines, the shadow\n// cameras, bounding boxes, normals, the axes, the orbit target - and a corner readout of what a frame\n// costs. view.explain() is the text an agent reads; this is what a person looks at. Loaded only when\n// debug is turned on. The helpers are drawn by the View after the scene, not added to it, so they\n// are never picked, saved, framed or counted: lines in the scene's depth, and faintly through\n// whatever is in front of them, so a light inside a lamp can still be found.\nimport { collect } from './frame.js'\nimport { Geometry } from './geometry/Geometry.js'\nimport { Lines } from './scene/drawables.js'\nimport { Scene } from './scene/Scene.js'\nimport { Box3 } from './value/Box3.js'\nimport { Mat4 } from './value/Mat4.js'\nimport { Vec3 } from './value/Vec3.js'\n\n/** @import { DrawItem, FrameDescription } from './frame.js' */\n/** @import { Hit } from './Raycaster.js' */\n/** @import { Camera } from './scene/cameras.js' */\n\n/**\n * What debug draws. `true` for a helper shows it for everything; `'hovered'` (bounds and normals) only\n * for what is under the pointer.\n * @typedef {object} DebugOptions\n * @property {boolean} [lights] each light: an arrow for directional ones, a small sphere and its range\n * for point lights, a cone for spot lights - in the light's colour\n * @property {boolean} [shadows] the box or cone each shadow is drawn from - the usual reason a shadow\n * is cut off or soft\n * @property {boolean | 'hovered'} [bounds] world bounding boxes - what culling and view.frame() see\n * @property {boolean | 'hovered'} [normals] short lines along vertex normals (meshes up to 5,000\n * vertices each when all are shown)\n * @property {boolean} [axes] x, y and z at the origin, red, green and blue\n * @property {boolean} [camera] the point the camera orbits round\n * @property {boolean | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'} [stats] a readout of\n * frame time, draw calls, triangles, programs, textures and geometries, in a corner of the canvas\n */\n\nconst COLORS = { shadow: [1, 0.8, 0], bounds: [0, 0.9, 1], normal: [1, 0.31, 0.85], camera: [1, 1, 1] }\n// normals, when shown for everything: past these a model would be a hedgehog\nconst NORMALS_PER_MESH = 5000, NORMALS_IN_ALL = 40000\n\nexport class DebugDraw {\n #scene = new Scene()\n #geometry = new Geometry({ positions: [0, 0, 0, 0, 0, 0] })\n // the same lines twice: in the scene's depth, and faintly through what is in front of them\n #solid = /** @type {Lines} */ (this.#scene.add(new Lines({ geometry: this.#geometry, pickable: false, frustumCulled: false,\n material: { vertexColors: true, width: 1.5, fog: false } })))\n #faint = /** @type {Lines} */ (this.#scene.add(new Lines({ geometry: this.#geometry, pickable: false, frustumCulled: false, renderOrder: 1e9,\n material: { vertexColors: true, width: 1, opacity: 0.22, depthTest: false, depthWrite: false, fog: false } })))\n /** @type {HTMLDivElement | null} */ #stats = null\n #statsAt = 0\n /** @type {number[]} */ #positions = []\n /** @type {number[]} */ #colors = []\n\n /** @param {HTMLElement | null} overlay where the stats readout goes - the layer over the canvas */\n constructor(overlay) {\n this.overlay = overlay\n }\n\n /**\n * Add the helpers to a frame about to be drawn, after everything in it. Returns how many items were\n * added to each list, to take out again once it is drawn.\n * @param {FrameDescription} frame @param {DebugOptions} o\n * @param {{ camera: Camera, hovered: Hit | null, bounds: Box3 }} at\n * @returns {{ opaque: number, transparent: number }}\n */\n add(frame, o, at) {\n this.#positions = []\n this.#colors = []\n const items = [...frame.opaque, ...frame.transparent]\n const size = at.bounds.isEmpty() ? 1 : Math.max(at.bounds.getRadius(), 1e-3)\n const middle = at.bounds.isEmpty() ? new Vec3() : at.bounds.getCenter()\n if (o.lights) this.#lights(frame, size, middle)\n if (o.shadows) this.#shadows(frame)\n if (o.bounds) this.#bounds(items, o.bounds === 'hovered' ? at.hovered : null, o.bounds === 'hovered')\n if (o.normals) this.#normals(items, o.normals === 'hovered' ? at.hovered : null, o.normals === 'hovered', size * 0.025)\n if (o.axes) {\n this.#segment([0, 0, 0], [size * 0.5, 0, 0], [1, 0.2, 0.2])\n this.#segment([0, 0, 0], [0, size * 0.5, 0], [0.3, 1, 0.3])\n this.#segment([0, 0, 0], [0, 0, size * 0.5], [0.3, 0.5, 1])\n }\n const target = /** @type {any} */ (at.camera).target\n if (o.camera && target) this.#cross(target, size * 0.04, COLORS.camera)\n if (!this.#positions.length) return { opaque: 0, transparent: 0 }\n\n // one buffer for all of it, made again only when it changes length\n const n = this.#positions.length\n const colors = this.#geometry.colors\n if (this.#geometry.positions.length === n && colors) {\n this.#geometry.positions.set(this.#positions)\n colors.set(this.#colors)\n this.#geometry.needsUpdate()\n } else {\n this.#geometry.dispose()\n this.#geometry = new Geometry({ positions: this.#positions, colors: this.#colors })\n this.#solid.geometry = this.#faint.geometry = this.#geometry\n }\n this.#scene.updateWorldTree()\n const drawn = collect(this.#scene, at.camera)\n frame.opaque.push(...drawn.opaque)\n frame.transparent.push(...drawn.transparent)\n return { opaque: drawn.opaque.length, transparent: drawn.transparent.length }\n }\n\n /**\n * The corner readout, refreshed a few times a second.\n * @param {DebugOptions['stats']} where\n * @param {{ drawCalls: number, triangles: number, programs: number, textures: number, geometries: number, compiling: number }} info\n * @param {number} frameMs time between drawn frames @param {number} cpuMs time the last frame took to draw\n */\n stats(where, info, frameMs, cpuMs) {\n if (!where || !this.overlay || typeof document === 'undefined') {\n this.#stats?.remove()\n this.#stats = null\n return\n }\n const corner = where === true ? 'top-left' : where\n if (!this.#stats) {\n this.#stats = document.createElement('div')\n this.#stats.className = 'phoria-debug-stats'\n Object.assign(this.#stats.style, { position: 'absolute', zIndex: '3', pointerEvents: 'none', padding: '6px 8px', borderRadius: '6px',\n background: 'rgba(10, 12, 16, 0.72)', color: '#e8ecf2', font: '11px/1.45 ui-monospace, SFMono-Regular, Consolas, monospace', whiteSpace: 'pre' })\n this.overlay.append(this.#stats)\n }\n const [v, h] = corner.split('-')\n Object.assign(this.#stats.style, { top: v === 'top' ? '8px' : '', bottom: v === 'bottom' ? '8px' : '', left: h === 'left' ? '8px' : '', right: h === 'right' ? '8px' : '' })\n const now = performance.now()\n if (now - this.#statsAt < 250) return\n this.#statsAt = now\n const k = (x) => (x >= 1e6 ? `${(x / 1e6).toFixed(1)}M` : x >= 1e4 ? `${Math.round(x / 1e3)}k` : String(x))\n const fps = frameMs > 0 ? Math.round(1000 / frameMs) : 0\n this.#stats.textContent = [\n `${fps ? `${fps} fps ${frameMs.toFixed(1)} ms` : 'idle'} cpu ${cpuMs.toFixed(1)} ms`,\n `${info.drawCalls} draws ${k(info.triangles)} triangles`,\n `${info.programs} programs${info.compiling ? ` (${info.compiling} compiling)` : ''} ${info.textures} textures ${info.geometries} geometries`,\n ].join('\\n')\n }\n\n dispose() {\n this.#stats?.remove()\n this.#stats = null\n this.#geometry.dispose()\n }\n\n // ---- the helpers, as line segments in world space, colours sRGB\n\n /** @param {ArrayLike<number>} a @param {ArrayLike<number>} b @param {ArrayLike<number>} c */\n #segment(a, b, c) {\n this.#positions.push(a[0], a[1], a[2], b[0], b[1], b[2])\n this.#colors.push(c[0], c[1], c[2], c[0], c[1], c[2])\n }\n\n /** @param {ArrayLike<number>} p @param {number} r @param {ArrayLike<number>} c */\n #cross(p, r, c) {\n for (let k = 0; k < 3; k++) {\n const a = [p[0], p[1], p[2]], b = [p[0], p[1], p[2]]\n a[k] -= r\n b[k] += r\n this.#segment(a, b, c)\n }\n }\n\n /** A circle round a centre, in the plane of two unit axes. @param {Vec3} centre @param {Vec3} u @param {Vec3} v @param {number} r @param {ArrayLike<number>} c */\n #circle(centre, u, v, r, c, segments = 32) {\n const at = (i) => {\n const a = i / segments * Math.PI * 2, x = Math.cos(a) * r, y = Math.sin(a) * r\n return [centre[0] + u[0] * x + v[0] * y, centre[1] + u[1] * x + v[1] * y, centre[2] + u[2] * x + v[2] * y]\n }\n for (let i = 0; i < segments; i++) this.#segment(at(i), at(i + 1), c)\n }\n\n /** The twelve edges of a box. @param {Box3} box @param {ArrayLike<number>} c */\n #box(box, c) {\n const { min: a, max: b } = box\n const corner = (i) => [i & 1 ? b[0] : a[0], i & 2 ? b[1] : a[1], i & 4 ? b[2] : a[2]]\n for (const [i, j] of EDGES) this.#segment(corner(i), corner(j), c)\n }\n\n /** The edges of what a view-projection matrix sees: a box for an orthographic one, a frustum for a perspective one. @param {ArrayLike<number>} viewProjection @param {ArrayLike<number>} c */\n #frustum(viewProjection, c) {\n const inverse = new Mat4().copy(viewProjection).invert()\n const corner = (i) => new Vec3(i & 1 ? 1 : -1, i & 2 ? 1 : -1, i & 4 ? 1 : -1).applyMatrix4(inverse)\n for (const [i, j] of EDGES) this.#segment(corner(i), corner(j), c)\n }\n\n /** @param {FrameDescription} frame @param {number} size @param {Vec3} middle */\n #lights(frame, size, middle) {\n for (const l of frame.lights) {\n const c = l.color.toSRGB().map((x) => Math.min(1, x))\n if (l.kind === 'directional' && l.direction) {\n // an arrow coming in over the scene's middle, the way the light travels\n const d = new Vec3().copy(l.direction).normalize()\n const from = moved(middle, d, -size * 1.1), to = moved(middle, d, -size * 0.45)\n this.#segment(from, to, c)\n const [u, v] = across(d)\n for (const s of [u, v, u.clone().negate(), v.clone().negate()]) {\n this.#segment(to, moved(moved(to, d, -size * 0.08), s, size * 0.04), c)\n }\n } else if (l.kind === 'point' && l.position) {\n const p = new Vec3().copy(l.position), r = size * 0.06\n this.#circle(p, X, Y, r, c, 16)\n this.#circle(p, X, Z, r, c, 16)\n this.#circle(p, Y, Z, r, c, 16)\n if (l.range) this.#circle(p, X, Z, l.range, c, 48)\n } else if (l.kind === 'spot' && l.position && l.direction) {\n // the cone out to its range (or the scene's size), with a ring where its edge is\n const p = new Vec3().copy(l.position), d = new Vec3().copy(l.direction).normalize()\n const length = l.range ? Math.min(l.range, size * 2) : size, r = length * Math.tan(l.angle ?? Math.PI / 4)\n const end = moved(p, d, length)\n const [u, v] = across(d)\n this.#circle(end, u, v, r, c, 32)\n for (const s of [u, v, u.clone().negate(), v.clone().negate()]) this.#segment(p, moved(end, s, r), c)\n this.#segment(p, end, c)\n }\n }\n }\n\n /** @param {FrameDescription} frame */\n #shadows(frame) {\n if (frame.shadow) this.#frustum(frame.shadow.viewProjection, COLORS.shadow)\n for (const s of frame.spotShadows) this.#frustum(s.viewProjection, COLORS.shadow)\n }\n\n /** @param {DrawItem[]} items @param {Hit | null} hovered @param {boolean} onlyHovered */\n #bounds(items, hovered, onlyHovered) {\n if (onlyHovered) {\n if (!hovered) return\n const node = /** @type {any} */ (hovered.node)\n // an instance's own box, or the whole node's\n if (hovered.instance !== null && typeof node.eachInstanceBox === 'function') {\n let i = 0\n node.eachInstanceBox((x0, y0, z0, x1, y1, z1) => {\n if (i++ === hovered.instance) this.#box(new Box3([x0, y0, z0], [x1, y1, z1]), COLORS.bounds)\n })\n } else this.#box(node.expandBounds(new Box3()), COLORS.bounds)\n return\n }\n const seen = new Set()\n for (const item of items) {\n if (seen.has(item.node)) continue\n seen.add(item.node)\n const box = /** @type {any} */ (item.node).expandBounds(new Box3())\n if (!box.isEmpty()) this.#box(box, COLORS.bounds)\n }\n }\n\n /** @param {DrawItem[]} items @param {Hit | null} hovered @param {boolean} onlyHovered @param {number} length */\n #normals(items, hovered, onlyHovered, length) {\n const meshes = onlyHovered\n ? (hovered && /** @type {any} */ (hovered.node).drawMode === 'triangles' ? [/** @type {any} */ (hovered.node)] : [])\n : [...new Set(items.map((i) => i.node))].filter((n) => /** @type {any} */ (n).drawMode === 'triangles')\n let budget = NORMALS_IN_ALL\n for (const node of /** @type {any[]} */ (meshes)) {\n if (node.instanceMatrices) continue\n const g = node.geometry, count = g.vertexCount\n if (!g.normals || (!onlyHovered && (count > NORMALS_PER_MESH || count > budget))) continue\n budget -= count\n const m = node.worldMatrix, p = g.positions, n = g.normals\n const a = new Vec3(), d = new Vec3()\n for (let i = 0; i < count; i++) {\n a.set(p[i * 3], p[i * 3 + 1], p[i * 3 + 2]).applyMatrix4(m)\n // the normal turned with the node (the upper 3x3: right for rotation and even scale)\n d.set(m[0] * n[i * 3] + m[4] * n[i * 3 + 1] + m[8] * n[i * 3 + 2], m[1] * n[i * 3] + m[5] * n[i * 3 + 1] + m[9] * n[i * 3 + 2],\n m[2] * n[i * 3] + m[6] * n[i * 3 + 1] + m[10] * n[i * 3 + 2]).normalize()\n this.#segment(a, [a[0] + d[0] * length, a[1] + d[1] * length, a[2] + d[2] * length], COLORS.normal)\n }\n }\n }\n}\n\nconst X = new Vec3(1, 0, 0), Y = new Vec3(0, 1, 0), Z = new Vec3(0, 0, 1)\n// a box's corners by bit (x, y, z), and the twelve edges between them\nconst EDGES = [[0, 1], [2, 3], [4, 5], [6, 7], [0, 2], [1, 3], [4, 6], [5, 7], [0, 4], [1, 5], [2, 6], [3, 7]]\n\n/** Two unit axes across a direction. @param {Vec3} d @returns {[Vec3, Vec3]} */\nfunction across(d) {\n const up = Math.abs(d[1]) < 0.9 ? Y : X\n const u = d.clone().cross(up).normalize()\n return [u, d.clone().cross(u).normalize()]\n}\n\n/** p moved s along d, as a new point. @param {ArrayLike<number>} p @param {ArrayLike<number>} d @param {number} s */\nfunction moved(p, d, s) {\n return new Vec3(p[0] + d[0] * s, p[1] + d[1] * s, p[2] + d[2] * s)\n}\n"],"mappings":"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"}
|
package/dist/edit.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { At as e,
|
|
1
|
+
import { At as e, Ft as t, Mt as n, Nt as r, Ot as i, it as a, jt as o, mt as s, st as c, u as l, v as u } from "./core.js";
|
|
2
2
|
// src/edit/TransformHandle.js
|
|
3
3
|
var d = [
|
|
4
4
|
"view",
|
|
@@ -43,23 +43,23 @@ var d = [
|
|
|
43
43
|
#n = null;
|
|
44
44
|
#r = {};
|
|
45
45
|
#i = null;
|
|
46
|
-
constructor(
|
|
47
|
-
if (n("TransformHandle",
|
|
48
|
-
n("TransformHandle",
|
|
49
|
-
let
|
|
50
|
-
let
|
|
51
|
-
return
|
|
46
|
+
constructor(i) {
|
|
47
|
+
if (n("TransformHandle", i, d, f), i?.view?.scene || t("TransformHandle", "'view' is required - the View it is used in"), this.#e = i.view, i.move !== void 0 && (this.move = i.move !== !1 && o("TransformHandle", "move", i.move, ["xz", "xyz"])), i.turn !== void 0 && (this.turn = _("turn", i.turn)), i.dragTarget !== void 0 && (this.dragTarget = _("dragTarget", i.dragTarget)), i.size !== void 0 && (this.size = e("TransformHandle", "size", i.size), this.size > 0 || t("TransformHandle", `'size' is in pixels, above 0, got ${this.size}`)), i.snap != null) {
|
|
48
|
+
n("TransformHandle", i.snap, ["move", "turn"], {}, "snap");
|
|
49
|
+
let e = (e) => {
|
|
50
|
+
let n = i.snap?.[e];
|
|
51
|
+
return n === void 0 ? 0 : (typeof n == "number" && n >= 0 || t("TransformHandle", `'snap.${e}' must be a step of 0 or more, got ${r(n)}`), n);
|
|
52
52
|
};
|
|
53
53
|
this.snap = {
|
|
54
|
-
move:
|
|
55
|
-
turn:
|
|
54
|
+
move: e("move"),
|
|
55
|
+
turn: e("turn")
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
for (let
|
|
59
|
-
let
|
|
60
|
-
|
|
58
|
+
for (let e of ["constrain", "onChange"]) {
|
|
59
|
+
let n = i[e];
|
|
60
|
+
n != null && (typeof n != "function" && t("TransformHandle", `'${e}' must be a function, got ${r(n)}`), this[e] = n);
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
i.history != null && (typeof i.history.transform != "function" && t("TransformHandle", `'history' must be a History, got ${r(i.history)}`), this.history = i.history), this.#a(), this.node.visible = !1, this.node.onUpdate(() => this.#o()), this.#e.scene.add(this.node), i.target != null && (this.target = i.target);
|
|
63
63
|
}
|
|
64
64
|
get target() {
|
|
65
65
|
return this.#t;
|
|
@@ -84,8 +84,8 @@ var d = [
|
|
|
84
84
|
0,
|
|
85
85
|
0,
|
|
86
86
|
0
|
|
87
|
-
],
|
|
88
|
-
let s = this.node.add(new
|
|
87
|
+
], o = t) => {
|
|
88
|
+
let s = this.node.add(new a({
|
|
89
89
|
name: t,
|
|
90
90
|
geometry: n,
|
|
91
91
|
material: e(r),
|
|
@@ -93,7 +93,7 @@ var d = [
|
|
|
93
93
|
renderOrder: m,
|
|
94
94
|
castShadow: !1
|
|
95
95
|
}));
|
|
96
|
-
return s.onDrag = (e) => this.#s(
|
|
96
|
+
return s.onDrag = (e) => this.#s(o, e), this.#r[t] = s, s;
|
|
97
97
|
};
|
|
98
98
|
t("ring", u({
|
|
99
99
|
radius: 1,
|
|
@@ -151,85 +151,85 @@ var d = [
|
|
|
151
151
|
let n = this.#e, r = n.camera, i = n.canvas.clientHeight || 1, a = r.constructor.type === "orthographic-camera" ? r.height / i : 2 * r.position.distanceTo(t) * Math.tan(r.fov * Math.PI / 360) / i, o = e.bounds, s = o.isEmpty() ? 0 : Math.hypot(o.max[0] - o.min[0], o.max[2] - o.min[2]) / 2, c = Math.max(this.size / 2 * a, s * 1.1);
|
|
152
152
|
this.node.scale.set(c, c, c);
|
|
153
153
|
}
|
|
154
|
-
#s(e,
|
|
155
|
-
let
|
|
156
|
-
if (!
|
|
157
|
-
if (
|
|
158
|
-
let
|
|
154
|
+
#s(e, t) {
|
|
155
|
+
let n = this.#t;
|
|
156
|
+
if (!n) return;
|
|
157
|
+
if (t.phase === "start") {
|
|
158
|
+
let r = n.worldPosition;
|
|
159
159
|
if (e === "y") {
|
|
160
|
-
let e = this.#e.camera.position,
|
|
161
|
-
|
|
162
|
-
normal:
|
|
163
|
-
point:
|
|
160
|
+
let e = this.#e.camera.position, n = new i(e[0] - r[0], 0, e[2] - r[2]);
|
|
161
|
+
t.plane = {
|
|
162
|
+
normal: n.len() > 1e-6 ? n.normalize() : new i(0, 0, 1),
|
|
163
|
+
point: r.clone()
|
|
164
164
|
};
|
|
165
|
-
} else
|
|
166
|
-
normal: new
|
|
167
|
-
point:
|
|
165
|
+
} else t.plane = {
|
|
166
|
+
normal: new i(0, 1, 0),
|
|
167
|
+
point: r.clone()
|
|
168
168
|
};
|
|
169
|
-
let a =
|
|
169
|
+
let a = t.ray.intersectPlane(t.plane.normal, t.plane.point), o = a === null ? r : t.ray.at(a);
|
|
170
170
|
this.#i = {
|
|
171
|
-
position:
|
|
172
|
-
rotation: g(
|
|
173
|
-
quaternion:
|
|
174
|
-
angle: Math.atan2(o[2] -
|
|
175
|
-
done: this.history ? this.history.transform(
|
|
176
|
-
}, this.onChange?.(
|
|
171
|
+
position: n.position.clone(),
|
|
172
|
+
rotation: g(n.quaternion),
|
|
173
|
+
quaternion: n.quaternion.clone(),
|
|
174
|
+
angle: Math.atan2(o[2] - r[2], o[0] - r[0]),
|
|
175
|
+
done: this.history ? this.history.transform(n) : null
|
|
176
|
+
}, this.onChange?.(n, "start");
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
|
-
let
|
|
180
|
-
if (!
|
|
181
|
-
if (
|
|
182
|
-
|
|
179
|
+
let r = this.#i;
|
|
180
|
+
if (!r) return;
|
|
181
|
+
if (t.phase === "end") {
|
|
182
|
+
r.done?.(e === "ring" ? "Turn" : "Move"), this.#i = null, this.onChange?.(n, "end");
|
|
183
183
|
return;
|
|
184
184
|
}
|
|
185
|
-
if (!
|
|
185
|
+
if (!t.point) return;
|
|
186
186
|
let a = {
|
|
187
|
-
position:
|
|
188
|
-
rotation:
|
|
187
|
+
position: r.position.clone(),
|
|
188
|
+
rotation: r.rotation
|
|
189
189
|
};
|
|
190
190
|
if (e === "ring") {
|
|
191
|
-
let e = this.node.position,
|
|
192
|
-
this.snap?.turn && (
|
|
191
|
+
let e = this.node.position, n = Math.atan2(t.point[2] - e[2], t.point[0] - e[0]), i = r.rotation - (n - r.angle);
|
|
192
|
+
this.snap?.turn && (i = Math.round(i / this.snap.turn) * this.snap.turn), a.rotation = i;
|
|
193
193
|
} else {
|
|
194
|
-
let
|
|
195
|
-
|
|
194
|
+
let n = t.delta, r = e === "x" ? [
|
|
195
|
+
n[0],
|
|
196
196
|
0,
|
|
197
197
|
0
|
|
198
198
|
] : e === "z" ? [
|
|
199
199
|
0,
|
|
200
200
|
0,
|
|
201
|
-
|
|
201
|
+
n[2]
|
|
202
202
|
] : e === "y" ? [
|
|
203
203
|
0,
|
|
204
|
-
|
|
204
|
+
n[1],
|
|
205
205
|
0
|
|
206
206
|
] : [
|
|
207
|
-
|
|
207
|
+
n[0],
|
|
208
208
|
0,
|
|
209
|
-
|
|
209
|
+
n[2]
|
|
210
210
|
];
|
|
211
211
|
a.position.add(r);
|
|
212
212
|
let i = this.snap?.move;
|
|
213
213
|
if (i) for (let e = 0; e < 3; e++) r[e] !== 0 && (a.position[e] = Math.round(a.position[e] / i) * i);
|
|
214
214
|
}
|
|
215
|
-
let o = this.constrain ? this.constrain(a,
|
|
215
|
+
let o = this.constrain ? this.constrain(a, n) : a;
|
|
216
216
|
if (o === null) return;
|
|
217
217
|
let c = o ?? a;
|
|
218
|
-
|
|
218
|
+
n.position.copy(c.position);
|
|
219
219
|
let l = new s().setFromAxisAngle([
|
|
220
220
|
0,
|
|
221
221
|
1,
|
|
222
222
|
0
|
|
223
|
-
], c.rotation -
|
|
224
|
-
|
|
223
|
+
], c.rotation - r.rotation);
|
|
224
|
+
n.quaternion.copy(l.multiply(r.quaternion)), this.#o(), this.onChange?.(n, "move");
|
|
225
225
|
}
|
|
226
226
|
};
|
|
227
227
|
function g(e) {
|
|
228
228
|
let [t, n, r, i] = e;
|
|
229
229
|
return Math.atan2(-2 * (t * r - n * i), 1 - 2 * (n * n + r * r));
|
|
230
230
|
}
|
|
231
|
-
function _(
|
|
232
|
-
return typeof n != "boolean" &&
|
|
231
|
+
function _(e, n) {
|
|
232
|
+
return typeof n != "boolean" && t("TransformHandle", `'${e}' must be true or false, got ${r(n)}`), n;
|
|
233
233
|
}
|
|
234
234
|
// src/edit/History.js
|
|
235
235
|
var v = ["limit", "onChange"], y = {
|
|
@@ -241,11 +241,11 @@ var v = ["limit", "onChange"], y = {
|
|
|
241
241
|
onChange = null;
|
|
242
242
|
#e = [];
|
|
243
243
|
#t = [];
|
|
244
|
-
constructor(
|
|
245
|
-
n("History",
|
|
244
|
+
constructor(e = {}) {
|
|
245
|
+
n("History", e, v, y), e.limit !== void 0 && ((!Number.isInteger(e.limit) || e.limit < 1) && t("History", `'limit' must be a whole number 1 or more, got ${r(e.limit)}`), this.limit = e.limit), e.onChange != null && (typeof e.onChange != "function" && t("History", `'onChange' must be a function, got ${r(e.onChange)}`), this.onChange = e.onChange);
|
|
246
246
|
}
|
|
247
|
-
push(
|
|
248
|
-
return
|
|
247
|
+
push(e) {
|
|
248
|
+
return e && typeof e.undo == "function" && typeof e.redo == "function" || t("History", `push() takes { label, undo, redo } with undo and redo functions, got ${r(e)}`), this.#e.push(e), this.#e.length > this.limit && this.#e.shift(), this.#t = [], this.onChange?.(this), this;
|
|
249
249
|
}
|
|
250
250
|
undo() {
|
|
251
251
|
let e = this.#e.pop();
|