@multiplekex/shallot 0.2.5 → 0.3.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/package.json +1 -1
- package/src/core/component.ts +1 -1
- package/src/core/index.ts +1 -13
- package/src/core/math.ts +186 -0
- package/src/core/state.ts +1 -1
- package/src/core/xml.ts +56 -41
- package/src/extras/orbit/index.ts +1 -1
- package/src/extras/text/index.ts +10 -65
- package/src/extras/{water.ts → water/index.ts} +59 -4
- package/src/standard/raster/batch.ts +149 -0
- package/src/standard/raster/forward.ts +832 -0
- package/src/standard/raster/index.ts +146 -472
- package/src/standard/raster/shadow.ts +408 -0
- package/src/standard/raytracing/bvh/blas.ts +335 -87
- package/src/standard/raytracing/bvh/radix.ts +225 -228
- package/src/standard/raytracing/bvh/refit.ts +711 -0
- package/src/standard/raytracing/bvh/structs.ts +0 -55
- package/src/standard/raytracing/bvh/tlas.ts +153 -141
- package/src/standard/raytracing/bvh/traverse.ts +72 -64
- package/src/standard/raytracing/index.ts +233 -204
- package/src/standard/raytracing/instance.ts +30 -18
- package/src/standard/raytracing/ray.ts +1 -1
- package/src/standard/raytracing/shaders.ts +23 -40
- package/src/standard/render/camera.ts +10 -28
- package/src/standard/render/data.ts +1 -1
- package/src/standard/render/index.ts +68 -12
- package/src/standard/render/light.ts +2 -2
- package/src/standard/render/mesh.ts +404 -0
- package/src/standard/render/overlay.ts +5 -2
- package/src/standard/render/postprocess.ts +263 -267
- package/src/standard/render/surface/index.ts +81 -12
- package/src/standard/render/surface/shaders.ts +265 -11
- package/src/standard/render/surface/structs.ts +10 -0
- package/src/standard/tween/tween.ts +44 -115
- package/src/standard/render/mesh/box.ts +0 -20
- package/src/standard/render/mesh/index.ts +0 -315
- package/src/standard/render/mesh/plane.ts +0 -11
- package/src/standard/render/mesh/sphere.ts +0 -40
- package/src/standard/render/mesh/unified.ts +0 -96
- package/src/standard/render/surface/compile.ts +0 -65
- package/src/standard/render/surface/noise.ts +0 -58
package/package.json
CHANGED
package/src/core/component.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface FieldAccessor {
|
|
|
11
11
|
|
|
12
12
|
export interface ComponentTraits {
|
|
13
13
|
defaults?: () => Record<string, number>;
|
|
14
|
-
|
|
14
|
+
parse?: Record<string, (value: string) => number | undefined>;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const traitsMap = new WeakMap<ComponentLike, ComponentTraits>();
|
package/src/core/index.ts
CHANGED
|
@@ -79,16 +79,4 @@ export { toKebabCase, toCamelCase } from "./strings";
|
|
|
79
79
|
|
|
80
80
|
export { initRuntime, getRuntime, resetRuntime, type Runtime, type RuntimeTarget } from "./runtime";
|
|
81
81
|
|
|
82
|
-
export {
|
|
83
|
-
parse,
|
|
84
|
-
serialize,
|
|
85
|
-
load,
|
|
86
|
-
registerPostLoadHook,
|
|
87
|
-
unregisterPostLoadHook,
|
|
88
|
-
type Node,
|
|
89
|
-
type Attr,
|
|
90
|
-
type Ref,
|
|
91
|
-
type ParseError,
|
|
92
|
-
type PostLoadHook,
|
|
93
|
-
type PostLoadContext,
|
|
94
|
-
} from "./xml";
|
|
82
|
+
export { parse, serialize, load, type Node, type Attr, type Ref, type ParseError } from "./xml";
|
package/src/core/math.ts
CHANGED
|
@@ -284,6 +284,192 @@ export function extractFrustumPlanes(viewProj: Float32Array): Float32Array {
|
|
|
284
284
|
return planes;
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
+
export function lookAtMatrix(
|
|
288
|
+
eyeX: number,
|
|
289
|
+
eyeY: number,
|
|
290
|
+
eyeZ: number,
|
|
291
|
+
targetX: number,
|
|
292
|
+
targetY: number,
|
|
293
|
+
targetZ: number,
|
|
294
|
+
upX = 0,
|
|
295
|
+
upY = 1,
|
|
296
|
+
upZ = 0
|
|
297
|
+
): Float32Array {
|
|
298
|
+
let zx = eyeX - targetX;
|
|
299
|
+
let zy = eyeY - targetY;
|
|
300
|
+
let zz = eyeZ - targetZ;
|
|
301
|
+
let zLen = Math.sqrt(zx * zx + zy * zy + zz * zz);
|
|
302
|
+
|
|
303
|
+
if (zLen < 1e-6) {
|
|
304
|
+
zx = 0;
|
|
305
|
+
zy = 0;
|
|
306
|
+
zz = 1;
|
|
307
|
+
} else {
|
|
308
|
+
zLen = 1 / zLen;
|
|
309
|
+
zx *= zLen;
|
|
310
|
+
zy *= zLen;
|
|
311
|
+
zz *= zLen;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
let xx = upY * zz - upZ * zy;
|
|
315
|
+
let xy = upZ * zx - upX * zz;
|
|
316
|
+
let xz = upX * zy - upY * zx;
|
|
317
|
+
let xLen = Math.sqrt(xx * xx + xy * xy + xz * xz);
|
|
318
|
+
|
|
319
|
+
if (xLen < 1e-6) {
|
|
320
|
+
if (Math.abs(zy) > 0.9) {
|
|
321
|
+
xx = 1;
|
|
322
|
+
xy = 0;
|
|
323
|
+
xz = 0;
|
|
324
|
+
} else {
|
|
325
|
+
xx = -zz;
|
|
326
|
+
xy = 0;
|
|
327
|
+
xz = zx;
|
|
328
|
+
}
|
|
329
|
+
xLen = Math.sqrt(xx * xx + xy * xy + xz * xz);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
xLen = 1 / xLen;
|
|
333
|
+
xx *= xLen;
|
|
334
|
+
xy *= xLen;
|
|
335
|
+
xz *= xLen;
|
|
336
|
+
|
|
337
|
+
const yx = zy * xz - zz * xy;
|
|
338
|
+
const yy = zz * xx - zx * xz;
|
|
339
|
+
const yz = zx * xy - zy * xx;
|
|
340
|
+
|
|
341
|
+
const tx = -(xx * eyeX + xy * eyeY + xz * eyeZ);
|
|
342
|
+
const ty = -(yx * eyeX + yy * eyeY + yz * eyeZ);
|
|
343
|
+
const tz = -(zx * eyeX + zy * eyeY + zz * eyeZ);
|
|
344
|
+
|
|
345
|
+
return new Float32Array([xx, yx, zx, 0, xy, yy, zy, 0, xz, yz, zz, 0, tx, ty, tz, 1]);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function orthographicBounds(
|
|
349
|
+
left: number,
|
|
350
|
+
right: number,
|
|
351
|
+
bottom: number,
|
|
352
|
+
top: number,
|
|
353
|
+
near: number,
|
|
354
|
+
far: number
|
|
355
|
+
): Float32Array {
|
|
356
|
+
const lr = 1 / (right - left);
|
|
357
|
+
const bt = 1 / (top - bottom);
|
|
358
|
+
const nf = 1 / (near - far);
|
|
359
|
+
|
|
360
|
+
return new Float32Array([
|
|
361
|
+
2 * lr,
|
|
362
|
+
0,
|
|
363
|
+
0,
|
|
364
|
+
0,
|
|
365
|
+
0,
|
|
366
|
+
2 * bt,
|
|
367
|
+
0,
|
|
368
|
+
0,
|
|
369
|
+
0,
|
|
370
|
+
0,
|
|
371
|
+
nf,
|
|
372
|
+
0,
|
|
373
|
+
-(right + left) * lr,
|
|
374
|
+
-(top + bottom) * bt,
|
|
375
|
+
near * nf,
|
|
376
|
+
1,
|
|
377
|
+
]);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function extractFrustumCorners(
|
|
381
|
+
invViewProj: Float32Array,
|
|
382
|
+
nearZ: number,
|
|
383
|
+
farZ: number
|
|
384
|
+
): Float32Array {
|
|
385
|
+
const corners = new Float32Array(24);
|
|
386
|
+
const ndcCorners = [
|
|
387
|
+
[-1, -1, nearZ],
|
|
388
|
+
[1, -1, nearZ],
|
|
389
|
+
[-1, 1, nearZ],
|
|
390
|
+
[1, 1, nearZ],
|
|
391
|
+
[-1, -1, farZ],
|
|
392
|
+
[1, -1, farZ],
|
|
393
|
+
[-1, 1, farZ],
|
|
394
|
+
[1, 1, farZ],
|
|
395
|
+
];
|
|
396
|
+
|
|
397
|
+
for (let i = 0; i < 8; i++) {
|
|
398
|
+
const [nx, ny, nz] = ndcCorners[i];
|
|
399
|
+
const m = invViewProj;
|
|
400
|
+
|
|
401
|
+
const wx = m[0] * nx + m[4] * ny + m[8] * nz + m[12];
|
|
402
|
+
const wy = m[1] * nx + m[5] * ny + m[9] * nz + m[13];
|
|
403
|
+
const wz = m[2] * nx + m[6] * ny + m[10] * nz + m[14];
|
|
404
|
+
const ww = m[3] * nx + m[7] * ny + m[11] * nz + m[15];
|
|
405
|
+
|
|
406
|
+
corners[i * 3] = wx / ww;
|
|
407
|
+
corners[i * 3 + 1] = wy / ww;
|
|
408
|
+
corners[i * 3 + 2] = wz / ww;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return corners;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function invertMatrix(m: Float32Array): Float32Array {
|
|
415
|
+
const out = new Float32Array(16);
|
|
416
|
+
|
|
417
|
+
const a00 = m[0],
|
|
418
|
+
a01 = m[1],
|
|
419
|
+
a02 = m[2],
|
|
420
|
+
a03 = m[3];
|
|
421
|
+
const a10 = m[4],
|
|
422
|
+
a11 = m[5],
|
|
423
|
+
a12 = m[6],
|
|
424
|
+
a13 = m[7];
|
|
425
|
+
const a20 = m[8],
|
|
426
|
+
a21 = m[9],
|
|
427
|
+
a22 = m[10],
|
|
428
|
+
a23 = m[11];
|
|
429
|
+
const a30 = m[12],
|
|
430
|
+
a31 = m[13],
|
|
431
|
+
a32 = m[14],
|
|
432
|
+
a33 = m[15];
|
|
433
|
+
|
|
434
|
+
const b00 = a00 * a11 - a01 * a10;
|
|
435
|
+
const b01 = a00 * a12 - a02 * a10;
|
|
436
|
+
const b02 = a00 * a13 - a03 * a10;
|
|
437
|
+
const b03 = a01 * a12 - a02 * a11;
|
|
438
|
+
const b04 = a01 * a13 - a03 * a11;
|
|
439
|
+
const b05 = a02 * a13 - a03 * a12;
|
|
440
|
+
const b06 = a20 * a31 - a21 * a30;
|
|
441
|
+
const b07 = a20 * a32 - a22 * a30;
|
|
442
|
+
const b08 = a20 * a33 - a23 * a30;
|
|
443
|
+
const b09 = a21 * a32 - a22 * a31;
|
|
444
|
+
const b10 = a21 * a33 - a23 * a31;
|
|
445
|
+
const b11 = a22 * a33 - a23 * a32;
|
|
446
|
+
|
|
447
|
+
let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
|
|
448
|
+
if (Math.abs(det) < 1e-10) {
|
|
449
|
+
return out;
|
|
450
|
+
}
|
|
451
|
+
det = 1 / det;
|
|
452
|
+
|
|
453
|
+
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
|
|
454
|
+
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
|
|
455
|
+
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
|
|
456
|
+
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
|
|
457
|
+
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
|
|
458
|
+
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
|
|
459
|
+
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
|
|
460
|
+
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
|
|
461
|
+
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
|
|
462
|
+
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
|
|
463
|
+
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
|
|
464
|
+
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
|
|
465
|
+
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
|
|
466
|
+
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
|
|
467
|
+
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
|
|
468
|
+
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
|
|
469
|
+
|
|
470
|
+
return out;
|
|
471
|
+
}
|
|
472
|
+
|
|
287
473
|
export function lookAt(
|
|
288
474
|
eyeX: number,
|
|
289
475
|
eyeY: number,
|
package/src/core/state.ts
CHANGED
|
@@ -20,7 +20,7 @@ import type { RelationDef } from "./relation";
|
|
|
20
20
|
import type { Plugin, StateBuilder } from "./builder";
|
|
21
21
|
import { initRuntime, type Runtime } from "./runtime";
|
|
22
22
|
import { registerComponent, getTraits, type ComponentData } from "./component";
|
|
23
|
-
import {
|
|
23
|
+
import type { ResourceKey } from "./resource";
|
|
24
24
|
|
|
25
25
|
export const MAX_ENTITIES = 65536;
|
|
26
26
|
|
package/src/core/xml.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { addComponent, Pair } from "bitecs";
|
|
2
2
|
import type { State } from "./state";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getRegisteredComponent,
|
|
5
|
+
getTraits,
|
|
6
|
+
type ComponentLike,
|
|
7
|
+
type RegisteredComponent,
|
|
8
|
+
} from "./component";
|
|
4
9
|
import { getRelationDef, ChildOf } from "./relation";
|
|
5
10
|
import { toKebabCase, toCamelCase } from "./strings";
|
|
6
11
|
|
|
@@ -136,7 +141,7 @@ function tokenize(xml: string): Token[] {
|
|
|
136
141
|
|
|
137
142
|
function parseTagAttrs(tag: string): Record<string, string> {
|
|
138
143
|
const attrs: Record<string, string> = {};
|
|
139
|
-
const attrRegex = /([^\s
|
|
144
|
+
const attrRegex = /([^\s=<>/]+)(?:\s*=\s*"([^"]*)")?/g;
|
|
140
145
|
const inner = tag.replace(/^<\s*\w+/, "").replace(/\/?>$/, "");
|
|
141
146
|
let match: RegExpExecArray | null;
|
|
142
147
|
|
|
@@ -312,23 +317,6 @@ function parseNodeFromTokens(
|
|
|
312
317
|
};
|
|
313
318
|
}
|
|
314
319
|
|
|
315
|
-
export interface PostLoadContext {
|
|
316
|
-
getEntityByName(name: string): number | null;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export type PostLoadHook = (state: State, context: PostLoadContext) => void;
|
|
320
|
-
|
|
321
|
-
const postLoadHooks: PostLoadHook[] = [];
|
|
322
|
-
|
|
323
|
-
export function registerPostLoadHook(hook: PostLoadHook): void {
|
|
324
|
-
postLoadHooks.push(hook);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
export function unregisterPostLoadHook(hook: PostLoadHook): void {
|
|
328
|
-
const idx = postLoadHooks.indexOf(hook);
|
|
329
|
-
if (idx !== -1) postLoadHooks.splice(idx, 1);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
320
|
interface QueuedEntity {
|
|
333
321
|
node: Node;
|
|
334
322
|
eid: number;
|
|
@@ -376,13 +364,6 @@ export function load(nodes: Node[], state: State): Map<Node, number> {
|
|
|
376
364
|
setFieldValue(ref.component, ref.field, ref.eid, targetEid);
|
|
377
365
|
}
|
|
378
366
|
|
|
379
|
-
const context: PostLoadContext = {
|
|
380
|
-
getEntityByName: (name) => nameToEntity.get(name) ?? null,
|
|
381
|
-
};
|
|
382
|
-
for (const hook of postLoadHooks) {
|
|
383
|
-
hook(state, context);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
367
|
if (errors.length > 0) {
|
|
387
368
|
throw new Error(errors.map((e) => e.message).join("\n"));
|
|
388
369
|
}
|
|
@@ -487,24 +468,22 @@ function applyComponent(
|
|
|
487
468
|
props["_value"] = value;
|
|
488
469
|
}
|
|
489
470
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
const result = parseAttrs(def, props);
|
|
497
|
-
values = result.values;
|
|
498
|
-
entityRefs = result.entityRefs;
|
|
499
|
-
for (const err of result.errors) {
|
|
500
|
-
errors.push({ message: `<${name}> ${err}` });
|
|
501
|
-
}
|
|
471
|
+
const result = parseAttrs(def, props);
|
|
472
|
+
const values = result.values;
|
|
473
|
+
const strings = result.strings;
|
|
474
|
+
const entityRefs = result.entityRefs;
|
|
475
|
+
for (const err of result.errors) {
|
|
476
|
+
errors.push({ message: `<${name}> ${err}` });
|
|
502
477
|
}
|
|
503
478
|
|
|
504
479
|
for (const [field, val] of Object.entries(values)) {
|
|
505
480
|
setFieldValue(component, field, eid, val);
|
|
506
481
|
}
|
|
507
482
|
|
|
483
|
+
for (const [field, val] of Object.entries(strings)) {
|
|
484
|
+
setString(component, field, eid, val);
|
|
485
|
+
}
|
|
486
|
+
|
|
508
487
|
for (const ref of entityRefs) {
|
|
509
488
|
pendingFieldRefs.push({
|
|
510
489
|
eid,
|
|
@@ -520,10 +499,12 @@ function parseAttrs(
|
|
|
520
499
|
props: Record<string, string>
|
|
521
500
|
): {
|
|
522
501
|
values: Record<string, number>;
|
|
502
|
+
strings: Record<string, string>;
|
|
523
503
|
entityRefs: { field: string; targetName: string }[];
|
|
524
504
|
errors: string[];
|
|
525
505
|
} {
|
|
526
506
|
const allValues: Record<string, number> = {};
|
|
507
|
+
const allStrings: Record<string, string> = {};
|
|
527
508
|
const allEntityRefs: { field: string; targetName: string }[] = [];
|
|
528
509
|
const allErrors: string[] = [];
|
|
529
510
|
|
|
@@ -531,6 +512,7 @@ function parseAttrs(
|
|
|
531
512
|
if (isCSSAttrSyntax(props._value)) {
|
|
532
513
|
const result = parsePropertyString(def.name, props._value, def.component);
|
|
533
514
|
Object.assign(allValues, result.values);
|
|
515
|
+
Object.assign(allStrings, result.strings);
|
|
534
516
|
allEntityRefs.push(...result.entityRefs);
|
|
535
517
|
allErrors.push(...result.errors);
|
|
536
518
|
}
|
|
@@ -543,6 +525,7 @@ function parseAttrs(
|
|
|
543
525
|
if (isCSSAttrSyntax(propValue)) {
|
|
544
526
|
const result = parsePropertyString(def.name, propValue, def.component);
|
|
545
527
|
Object.assign(allValues, result.values);
|
|
528
|
+
Object.assign(allStrings, result.strings);
|
|
546
529
|
allEntityRefs.push(...result.entityRefs);
|
|
547
530
|
allErrors.push(...result.errors);
|
|
548
531
|
} else {
|
|
@@ -552,12 +535,13 @@ function parseAttrs(
|
|
|
552
535
|
def.component
|
|
553
536
|
);
|
|
554
537
|
Object.assign(allValues, result.values);
|
|
538
|
+
Object.assign(allStrings, result.strings);
|
|
555
539
|
allEntityRefs.push(...result.entityRefs);
|
|
556
540
|
allErrors.push(...result.errors);
|
|
557
541
|
}
|
|
558
542
|
}
|
|
559
543
|
|
|
560
|
-
return { values: allValues, entityRefs: allEntityRefs, errors: allErrors };
|
|
544
|
+
return { values: allValues, strings: allStrings, entityRefs: allEntityRefs, errors: allErrors };
|
|
561
545
|
}
|
|
562
546
|
|
|
563
547
|
function setFieldValue(component: ComponentLike, field: string, eid: number, value: number): void {
|
|
@@ -567,6 +551,17 @@ function setFieldValue(component: ComponentLike, field: string, eid: number, val
|
|
|
567
551
|
}
|
|
568
552
|
}
|
|
569
553
|
|
|
554
|
+
function isStringField(component: ComponentLike, field: string): boolean {
|
|
555
|
+
const val = component[field];
|
|
556
|
+
if (val == null) return false;
|
|
557
|
+
if (ArrayBuffer.isView(val) || Array.isArray(val)) return false;
|
|
558
|
+
return typeof val === "object";
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function setString(component: ComponentLike, field: string, eid: number, value: string): void {
|
|
562
|
+
(component[field] as Record<number, string>)[eid] = value;
|
|
563
|
+
}
|
|
564
|
+
|
|
570
565
|
function detectVec2(component: ComponentLike, base: string): boolean {
|
|
571
566
|
return `${base}X` in component && `${base}Y` in component;
|
|
572
567
|
}
|
|
@@ -594,7 +589,7 @@ function parseNumber(value: string): number | null {
|
|
|
594
589
|
if (value === "false") return 0;
|
|
595
590
|
|
|
596
591
|
const num = parseFloat(value);
|
|
597
|
-
return isNaN(num) ? null : num;
|
|
592
|
+
return Number.isNaN(num) ? null : num;
|
|
598
593
|
}
|
|
599
594
|
|
|
600
595
|
function parseValues(valueStr: string): (number | null)[] {
|
|
@@ -633,10 +628,12 @@ function parsePropertyString(
|
|
|
633
628
|
component: ComponentLike
|
|
634
629
|
): {
|
|
635
630
|
values: Record<string, number>;
|
|
631
|
+
strings: Record<string, string>;
|
|
636
632
|
entityRefs: { field: string; targetName: string }[];
|
|
637
633
|
errors: string[];
|
|
638
634
|
} {
|
|
639
635
|
const values: Record<string, number> = {};
|
|
636
|
+
const strings: Record<string, string> = {};
|
|
640
637
|
const entityRefs: { field: string; targetName: string }[] = [];
|
|
641
638
|
const errors: string[] = [];
|
|
642
639
|
|
|
@@ -679,6 +676,24 @@ function parsePropertyString(
|
|
|
679
676
|
const parsed = parseValues(valueStr);
|
|
680
677
|
|
|
681
678
|
if (parsed.some((v) => v === null)) {
|
|
679
|
+
const rawValue = valueStr.trim();
|
|
680
|
+
|
|
681
|
+
if (name in component && isStringField(component, name)) {
|
|
682
|
+
strings[name] = rawValue;
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (parsed.length === 1) {
|
|
687
|
+
const traits = getTraits(component);
|
|
688
|
+
const parseFn = traits?.parse?.[name];
|
|
689
|
+
if (parseFn) {
|
|
690
|
+
const resolved = parseFn(rawValue);
|
|
691
|
+
if (resolved !== undefined) {
|
|
692
|
+
values[name] = resolved;
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
682
697
|
errors.push(`Invalid number in "${prop}"`);
|
|
683
698
|
continue;
|
|
684
699
|
}
|
|
@@ -756,7 +771,7 @@ function parsePropertyString(
|
|
|
756
771
|
}
|
|
757
772
|
}
|
|
758
773
|
|
|
759
|
-
return { values, entityRefs, errors };
|
|
774
|
+
return { values, strings, entityRefs, errors };
|
|
760
775
|
}
|
|
761
776
|
|
|
762
777
|
function isCSSAttrSyntax(value: string): boolean {
|
package/src/extras/text/index.ts
CHANGED
|
@@ -3,12 +3,10 @@ import { SDFGenerator } from "./sdf";
|
|
|
3
3
|
import {
|
|
4
4
|
MAX_ENTITIES,
|
|
5
5
|
resource,
|
|
6
|
-
registerPostLoadHook,
|
|
7
6
|
createFieldProxy,
|
|
8
7
|
type Plugin,
|
|
9
8
|
type State,
|
|
10
9
|
type System,
|
|
11
|
-
type PostLoadContext,
|
|
12
10
|
type FieldProxy,
|
|
13
11
|
} from "../../core";
|
|
14
12
|
import { setTraits } from "../../core/component";
|
|
@@ -31,14 +29,16 @@ const SDF_SIZE = 96;
|
|
|
31
29
|
const SDF_EXPONENT = 9;
|
|
32
30
|
const fontUrls: string[] = [];
|
|
33
31
|
const loadedFonts: (Font | null)[] = [];
|
|
32
|
+
const fontNames = new Map<string, number>();
|
|
34
33
|
|
|
35
34
|
export const DEFAULT_FONT =
|
|
36
35
|
"https://fonts.gstatic.com/s/inter/v20/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfMZg.ttf";
|
|
37
36
|
|
|
38
|
-
export function font(url: string): number {
|
|
37
|
+
export function font(url: string, name?: string): number {
|
|
39
38
|
const id = fontUrls.length;
|
|
40
39
|
fontUrls.push(url);
|
|
41
40
|
loadedFonts.push(null);
|
|
41
|
+
if (name) fontNames.set(name, id);
|
|
42
42
|
return id;
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -46,9 +46,14 @@ export function getFont(id: number): Font | null {
|
|
|
46
46
|
return loadedFonts[id] ?? null;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
export function getFontByName(name: string): number | undefined {
|
|
50
|
+
return fontNames.get(name);
|
|
51
|
+
}
|
|
52
|
+
|
|
49
53
|
export function resetFonts(): void {
|
|
50
54
|
fontUrls.length = 0;
|
|
51
55
|
loadedFonts.length = 0;
|
|
56
|
+
fontNames.clear();
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
async function loadFonts(): Promise<void> {
|
|
@@ -141,35 +146,6 @@ export const Text = {
|
|
|
141
146
|
colorB: createFieldProxy(data, 12, 10),
|
|
142
147
|
};
|
|
143
148
|
|
|
144
|
-
interface PendingText {
|
|
145
|
-
readonly eid: number;
|
|
146
|
-
readonly content: string;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
let pendingTextContent: PendingText[] = [];
|
|
150
|
-
|
|
151
|
-
function parseTextAttrs(attrs: Record<string, string>): Record<string, string> {
|
|
152
|
-
if (attrs._value) {
|
|
153
|
-
const parsed: Record<string, string> = {};
|
|
154
|
-
for (const part of attrs._value.split(";")) {
|
|
155
|
-
const colonIdx = part.indexOf(":");
|
|
156
|
-
if (colonIdx === -1) continue;
|
|
157
|
-
const key = part.slice(0, colonIdx).trim();
|
|
158
|
-
const value = part.slice(colonIdx + 1).trim();
|
|
159
|
-
if (key && value) parsed[key] = value;
|
|
160
|
-
}
|
|
161
|
-
return parsed;
|
|
162
|
-
}
|
|
163
|
-
return attrs;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function finalizePendingText(_state: State, _context: PostLoadContext): void {
|
|
167
|
-
for (const pending of pendingTextContent) {
|
|
168
|
-
Text.content[pending.eid] = pending.content;
|
|
169
|
-
}
|
|
170
|
-
pendingTextContent = [];
|
|
171
|
-
}
|
|
172
|
-
|
|
173
149
|
setTraits(Text, {
|
|
174
150
|
defaults: () => ({
|
|
175
151
|
font: 0,
|
|
@@ -180,36 +156,7 @@ setTraits(Text, {
|
|
|
180
156
|
anchorY: 0,
|
|
181
157
|
color: 0xffffff,
|
|
182
158
|
}),
|
|
183
|
-
|
|
184
|
-
const parsed = parseTextAttrs(attrs);
|
|
185
|
-
const result: Record<string, number> = {};
|
|
186
|
-
|
|
187
|
-
if (parsed.content) {
|
|
188
|
-
pendingTextContent.push({ eid, content: parsed.content });
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (parsed.font) result.font = parseInt(parsed.font, 10);
|
|
192
|
-
if (parsed["font-size"]) result.fontSize = parseFloat(parsed["font-size"]);
|
|
193
|
-
if (parsed.fontSize) result.fontSize = parseFloat(parsed.fontSize);
|
|
194
|
-
if (parsed.opacity) result.opacity = parseFloat(parsed.opacity);
|
|
195
|
-
if (parsed.visible) result.visible = parseFloat(parsed.visible);
|
|
196
|
-
if (parsed["anchor-x"]) result.anchorX = parseFloat(parsed["anchor-x"]);
|
|
197
|
-
if (parsed.anchorX) result.anchorX = parseFloat(parsed.anchorX);
|
|
198
|
-
if (parsed["anchor-y"]) result.anchorY = parseFloat(parsed["anchor-y"]);
|
|
199
|
-
if (parsed.anchorY) result.anchorY = parseFloat(parsed.anchorY);
|
|
200
|
-
if (parsed.color) {
|
|
201
|
-
const colorStr = parsed.color;
|
|
202
|
-
if (colorStr.startsWith("0x") || colorStr.startsWith("0X")) {
|
|
203
|
-
result.color = parseInt(colorStr, 16);
|
|
204
|
-
} else if (colorStr.startsWith("#")) {
|
|
205
|
-
result.color = parseInt(colorStr.slice(1), 16);
|
|
206
|
-
} else {
|
|
207
|
-
result.color = parseInt(colorStr, 10);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return result;
|
|
212
|
-
},
|
|
159
|
+
parse: { font: getFontByName },
|
|
213
160
|
});
|
|
214
161
|
|
|
215
162
|
interface GlyphMetrics {
|
|
@@ -669,7 +616,7 @@ interface PendingGlyph {
|
|
|
669
616
|
a: number;
|
|
670
617
|
}
|
|
671
618
|
|
|
672
|
-
|
|
619
|
+
const glyphsByFont: PendingGlyph[][] = [];
|
|
673
620
|
|
|
674
621
|
const TextSystem: System = {
|
|
675
622
|
group: "draw",
|
|
@@ -788,8 +735,6 @@ export const TextPlugin: Plugin = {
|
|
|
788
735
|
dependencies: [ComputePlugin, RenderPlugin],
|
|
789
736
|
|
|
790
737
|
async initialize(state: State) {
|
|
791
|
-
registerPostLoadHook(finalizePendingText);
|
|
792
|
-
|
|
793
738
|
const compute = Compute.from(state);
|
|
794
739
|
const render = Render.from(state);
|
|
795
740
|
if (!compute || !render) return;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { Plugin, State, System } from "../../core";
|
|
2
|
+
import { setTraits } from "../../core/component";
|
|
3
|
+
import { Transform } from "../../standard/transforms";
|
|
4
|
+
import { Mesh, Volume, mesh, Surface, surface, RenderPlugin } from "../../standard/render";
|
|
5
|
+
import type { MeshData } from "../../standard/render";
|
|
3
6
|
|
|
4
7
|
const SEED1 = "vec2(127.1, 311.7)";
|
|
5
8
|
const SEED2 = "vec2(269.5, 183.3)";
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
const waterSurface = surface({
|
|
8
11
|
fragment: `
|
|
9
12
|
let p = (*surface).worldPos.xz * 3.0;
|
|
10
13
|
let t = scene.time;
|
|
@@ -21,7 +24,7 @@ export const Water = surface({
|
|
|
21
24
|
(*surface).normal = normalize(vec3(-dx * 0.015, 1.0, -dz * 0.015));`,
|
|
22
25
|
});
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
function createSubdividedPlane(subdivisions: number = 32): MeshData {
|
|
25
28
|
const segments = subdivisions;
|
|
26
29
|
const vertexCount = (segments + 1) * (segments + 1);
|
|
27
30
|
const indexCount = segments * segments * 6;
|
|
@@ -62,3 +65,55 @@ export function createSubdividedPlane(subdivisions: number = 32): MeshData {
|
|
|
62
65
|
|
|
63
66
|
return { vertices, indices, vertexCount, indexCount };
|
|
64
67
|
}
|
|
68
|
+
|
|
69
|
+
const waterMesh = mesh(createSubdividedPlane(64));
|
|
70
|
+
|
|
71
|
+
export const Water = {
|
|
72
|
+
color: [] as number[],
|
|
73
|
+
opacity: [] as number[],
|
|
74
|
+
roughness: [] as number[],
|
|
75
|
+
metallic: [] as number[],
|
|
76
|
+
ior: [] as number[],
|
|
77
|
+
level: [] as number[],
|
|
78
|
+
};
|
|
79
|
+
setTraits(Water, {
|
|
80
|
+
defaults: () => ({
|
|
81
|
+
color: 0x4090a0,
|
|
82
|
+
opacity: 0.5,
|
|
83
|
+
roughness: 0.05,
|
|
84
|
+
metallic: 0.8,
|
|
85
|
+
ior: 1.33,
|
|
86
|
+
level: 0,
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const WaterSystem: System = {
|
|
91
|
+
group: "simulation",
|
|
92
|
+
|
|
93
|
+
update(state: State) {
|
|
94
|
+
for (const eid of state.query([Water])) {
|
|
95
|
+
if (!state.hasComponent(eid, Transform)) state.addComponent(eid, Transform);
|
|
96
|
+
if (!state.hasComponent(eid, Mesh)) state.addComponent(eid, Mesh);
|
|
97
|
+
if (!state.hasComponent(eid, Surface)) state.addComponent(eid, Surface);
|
|
98
|
+
|
|
99
|
+
Transform.posY[eid] = Water.level[eid];
|
|
100
|
+
Mesh.shape[eid] = waterMesh;
|
|
101
|
+
Mesh.color[eid] = Water.color[eid];
|
|
102
|
+
Mesh.opacity[eid] = Water.opacity[eid];
|
|
103
|
+
Mesh.roughness[eid] = Water.roughness[eid];
|
|
104
|
+
Mesh.metallic[eid] = Water.metallic[eid];
|
|
105
|
+
Mesh.ior[eid] = Water.ior[eid];
|
|
106
|
+
Mesh.sizeX[eid] = 40;
|
|
107
|
+
Mesh.sizeY[eid] = 1;
|
|
108
|
+
Mesh.sizeZ[eid] = 40;
|
|
109
|
+
Mesh.volume[eid] = Volume.HalfSpace;
|
|
110
|
+
Surface.type[eid] = waterSurface;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const WaterPlugin: Plugin = {
|
|
116
|
+
systems: [WaterSystem],
|
|
117
|
+
components: { Water },
|
|
118
|
+
dependencies: [RenderPlugin],
|
|
119
|
+
};
|