@multiplekex/shallot 0.1.7 → 0.1.10
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 +2 -2
- package/src/core/component.ts +7 -17
- package/src/core/index.ts +2 -1
- package/src/core/math.ts +30 -5
- package/src/core/state.ts +4 -2
- package/src/core/types.ts +2 -2
- package/src/core/xml.ts +83 -33
- package/src/extras/arrows/index.ts +79 -96
- package/src/extras/gradient/index.ts +1050 -0
- package/src/extras/index.ts +1 -0
- package/src/extras/lines/index.ts +103 -57
- package/src/extras/orbit/index.ts +1 -1
- package/src/extras/text/index.ts +249 -82
- package/src/standard/compute/graph.ts +10 -12
- package/src/standard/compute/index.ts +27 -2
- package/src/standard/compute/inspect.ts +49 -53
- package/src/standard/compute/pass.ts +23 -0
- package/src/standard/render/camera.ts +14 -2
- package/src/standard/render/forward.ts +104 -43
- package/src/standard/render/index.ts +85 -12
- package/src/standard/render/mesh/index.ts +255 -28
- package/src/standard/render/pass.ts +63 -0
- package/src/standard/render/postprocess.ts +250 -58
- package/src/standard/render/scene.ts +98 -2
- package/src/standard/render/surface/compile.ts +74 -0
- package/src/standard/render/surface/index.ts +116 -0
- package/src/standard/render/surface/structs.ts +50 -0
- package/src/standard/render/transparent.ts +91 -0
- package/src/standard/tween/sequence.ts +2 -2
- package/src/standard/tween/tween.ts +31 -13
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const WGSL_STRUCTS = /* wgsl */ `
|
|
2
|
+
struct VertexInput {
|
|
3
|
+
@location(0) position: vec3<f32>,
|
|
4
|
+
@location(1) normal: vec3<f32>,
|
|
5
|
+
@builtin(instance_index) instance: u32,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
struct VertexOutput {
|
|
9
|
+
@builtin(position) position: vec4<f32>,
|
|
10
|
+
@location(0) color: vec4<f32>,
|
|
11
|
+
@location(1) worldNormal: vec3<f32>,
|
|
12
|
+
@location(2) @interpolate(flat) entityId: u32,
|
|
13
|
+
@location(3) worldPos: vec3<f32>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
struct SurfaceData {
|
|
17
|
+
baseColor: vec3<f32>,
|
|
18
|
+
roughness: f32,
|
|
19
|
+
metallic: f32,
|
|
20
|
+
emission: vec3<f32>,
|
|
21
|
+
normal: vec3<f32>,
|
|
22
|
+
worldPos: vec3<f32>,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
struct FragmentOutput {
|
|
26
|
+
@location(0) color: vec4<f32>,
|
|
27
|
+
@location(1) entityId: u32,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
struct Scene {
|
|
31
|
+
viewProj: mat4x4<f32>,
|
|
32
|
+
cameraWorld: mat4x4<f32>,
|
|
33
|
+
ambientColor: vec4<f32>,
|
|
34
|
+
sunDirection: vec4<f32>,
|
|
35
|
+
sunColor: vec4<f32>,
|
|
36
|
+
cameraMode: f32,
|
|
37
|
+
cameraSize: f32,
|
|
38
|
+
viewport: vec2<f32>,
|
|
39
|
+
frustumPlanes: array<vec4<f32>, 6>,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@group(0) @binding(0) var<uniform> scene: Scene;
|
|
43
|
+
@group(0) @binding(1) var<storage, read> entityIds: array<u32>;
|
|
44
|
+
@group(0) @binding(2) var<storage, read> matrices: array<mat4x4<f32>>;
|
|
45
|
+
@group(0) @binding(3) var<storage, read> colors: array<vec4<f32>>;
|
|
46
|
+
@group(0) @binding(4) var<storage, read> sizes: array<vec4<f32>>;
|
|
47
|
+
@group(0) @binding(5) var<storage, read> pbr: array<vec4<f32>>;
|
|
48
|
+
@group(0) @binding(6) var<storage, read> emission: array<vec4<f32>>;
|
|
49
|
+
@group(0) @binding(7) var<storage, read> shapes: array<u32>;
|
|
50
|
+
`;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { State } from "../../core";
|
|
2
|
+
import type { ComputeNode, ExecutionContext } from "../compute";
|
|
3
|
+
import { MASK_FORMAT } from "./scene";
|
|
4
|
+
import { Pass, getDrawsByPass, type DrawContext, type SharedPassContext } from "./pass";
|
|
5
|
+
|
|
6
|
+
export { MASK_FORMAT };
|
|
7
|
+
|
|
8
|
+
export interface TransparentNodeConfig {
|
|
9
|
+
state: State;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createTransparentNode(config: TransparentNodeConfig): ComputeNode {
|
|
13
|
+
return {
|
|
14
|
+
id: "transparent",
|
|
15
|
+
pass: Pass.Transparent,
|
|
16
|
+
inputs: [{ id: "depth", access: "read" }],
|
|
17
|
+
outputs: [
|
|
18
|
+
{ id: "scene", access: "write" },
|
|
19
|
+
{ id: "mask", access: "write" },
|
|
20
|
+
],
|
|
21
|
+
|
|
22
|
+
execute(ctx: ExecutionContext) {
|
|
23
|
+
const { device, encoder, format, context } = ctx;
|
|
24
|
+
const targetView = ctx.getTextureView("scene") ?? ctx.canvasView;
|
|
25
|
+
const depthView = ctx.getTextureView("depth")!;
|
|
26
|
+
const maskView = ctx.getTextureView("mask")!;
|
|
27
|
+
const entityIdView = ctx.getTextureView("entityId")!;
|
|
28
|
+
|
|
29
|
+
const drawCtx: DrawContext = {
|
|
30
|
+
device,
|
|
31
|
+
encoder,
|
|
32
|
+
format,
|
|
33
|
+
width: context.canvas.width,
|
|
34
|
+
height: context.canvas.height,
|
|
35
|
+
sceneView: targetView,
|
|
36
|
+
depthView,
|
|
37
|
+
entityIdView,
|
|
38
|
+
maskView,
|
|
39
|
+
canvasView: ctx.canvasView,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const beforeTransparents = getDrawsByPass(config.state, Pass.BeforeTransparent);
|
|
43
|
+
for (const draw of beforeTransparents) {
|
|
44
|
+
draw.execute(drawCtx);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const draws = getDrawsByPass(config.state, Pass.Transparent);
|
|
48
|
+
if (draws.length > 0) {
|
|
49
|
+
const pass = encoder.beginRenderPass({
|
|
50
|
+
colorAttachments: [
|
|
51
|
+
{
|
|
52
|
+
view: targetView,
|
|
53
|
+
loadOp: "load" as const,
|
|
54
|
+
storeOp: "store" as const,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
view: maskView,
|
|
58
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
59
|
+
loadOp: "clear" as const,
|
|
60
|
+
storeOp: "store" as const,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
depthStencilAttachment: {
|
|
64
|
+
view: depthView,
|
|
65
|
+
depthLoadOp: "load" as const,
|
|
66
|
+
depthStoreOp: "store" as const,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const sharedCtx: SharedPassContext = {
|
|
71
|
+
device,
|
|
72
|
+
format,
|
|
73
|
+
maskFormat: MASK_FORMAT,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
for (const draw of draws) {
|
|
77
|
+
if (draw.draw) {
|
|
78
|
+
draw.draw(pass, sharedCtx);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
pass.end();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const afterTransparents = getDrawsByPass(config.state, Pass.AfterTransparent);
|
|
86
|
+
for (const draw of afterTransparents) {
|
|
87
|
+
draw.execute(drawCtx);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Pair } from "bitecs";
|
|
2
2
|
import { setTraits } from "../../core/component";
|
|
3
|
-
import { ChildOf, type State, type
|
|
3
|
+
import { ChildOf, type State, type PostLoadContext } from "../../core";
|
|
4
4
|
import { Tween, TweenState, ensureResolved, captureFromValue } from "./tween";
|
|
5
5
|
|
|
6
6
|
const compareNumbers = (a: number, b: number) => a - b;
|
|
@@ -35,7 +35,7 @@ setTraits(Sequence, {
|
|
|
35
35
|
}),
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
export function finalizeSequences(_state: State, _context:
|
|
38
|
+
export function finalizeSequences(_state: State, _context: PostLoadContext): void {}
|
|
39
39
|
|
|
40
40
|
function getChildrenSorted(state: State, parentEid: number): number[] {
|
|
41
41
|
childrenBuffer.length = 0;
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
registerPostLoadHook,
|
|
4
4
|
type State,
|
|
5
5
|
type System,
|
|
6
|
-
type ParseContext,
|
|
7
6
|
type Plugin,
|
|
7
|
+
type PostLoadContext,
|
|
8
8
|
} from "../../core";
|
|
9
9
|
import { setTraits } from "../../core/component";
|
|
10
10
|
import { getEasingIndex, getEasing } from "./easing";
|
|
@@ -57,7 +57,7 @@ setTraits(Tween, {
|
|
|
57
57
|
delay: 0,
|
|
58
58
|
easingIndex: 0,
|
|
59
59
|
}),
|
|
60
|
-
adapter: (attrs: Record<string, string>,
|
|
60
|
+
adapter: (attrs: Record<string, string>, eid: number) => {
|
|
61
61
|
const parsed = parseTweenAttrs(attrs);
|
|
62
62
|
const result: Record<string, number> = {};
|
|
63
63
|
|
|
@@ -66,7 +66,7 @@ setTraits(Tween, {
|
|
|
66
66
|
if (parsed.easing) result.easingIndex = getEasingIndex(parsed.easing);
|
|
67
67
|
|
|
68
68
|
if (parsed.target) {
|
|
69
|
-
setupTweenFromXml(
|
|
69
|
+
setupTweenFromXml(parsed, eid);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
return result;
|
|
@@ -110,12 +110,7 @@ interface PendingTween {
|
|
|
110
110
|
|
|
111
111
|
let pendingXmlTweens: PendingTween[] = [];
|
|
112
112
|
|
|
113
|
-
function setupTweenFromXml(
|
|
114
|
-
_state: State,
|
|
115
|
-
_context: ParseContext,
|
|
116
|
-
attrs: Record<string, string>,
|
|
117
|
-
tweenEid: number
|
|
118
|
-
): void {
|
|
113
|
+
function setupTweenFromXml(attrs: Record<string, string>, tweenEid: number): void {
|
|
119
114
|
pendingXmlTweens.push({
|
|
120
115
|
tweenEid,
|
|
121
116
|
target: attrs.target,
|
|
@@ -123,7 +118,7 @@ function setupTweenFromXml(
|
|
|
123
118
|
});
|
|
124
119
|
}
|
|
125
120
|
|
|
126
|
-
export function finalizePendingTweens(state: State, context:
|
|
121
|
+
export function finalizePendingTweens(state: State, context: PostLoadContext): void {
|
|
127
122
|
for (const pending of pendingXmlTweens) {
|
|
128
123
|
const parsed = parseTargetPath(pending.target);
|
|
129
124
|
if (!parsed) continue;
|
|
@@ -135,7 +130,14 @@ export function finalizePendingTweens(state: State, context: ParseContext): void
|
|
|
135
130
|
if (!binding) continue;
|
|
136
131
|
|
|
137
132
|
state.addRelation(pending.tweenEid, TweenTarget, targetEid);
|
|
138
|
-
|
|
133
|
+
const toValue =
|
|
134
|
+
pending.to.startsWith("0x") || pending.to.startsWith("0X")
|
|
135
|
+
? parseInt(pending.to, 16)
|
|
136
|
+
: parseFloat(pending.to);
|
|
137
|
+
if (!Number.isFinite(toValue)) {
|
|
138
|
+
throw new Error(`Tween has invalid 'to' value: "${pending.to}" (parsed as ${toValue})`);
|
|
139
|
+
}
|
|
140
|
+
Tween.to[pending.tweenEid] = toValue;
|
|
139
141
|
}
|
|
140
142
|
pendingXmlTweens = [];
|
|
141
143
|
}
|
|
@@ -159,8 +161,12 @@ export function ensureResolved(state: State, tweenEid: number): void {
|
|
|
159
161
|
const binding = state.getFieldAccessor(tweenEid);
|
|
160
162
|
|
|
161
163
|
if (binding && targetEid >= 0) {
|
|
164
|
+
const toValue = Tween.to[tweenEid];
|
|
165
|
+
if (!Number.isFinite(toValue)) {
|
|
166
|
+
throw new Error(`Tween ${tweenEid} has invalid to value: ${toValue}`);
|
|
167
|
+
}
|
|
162
168
|
Tween.from[tweenEid] = binding.get(targetEid) ?? 0;
|
|
163
|
-
binding.set(targetEid,
|
|
169
|
+
binding.set(targetEid, toValue);
|
|
164
170
|
}
|
|
165
171
|
|
|
166
172
|
Tween.elapsed[tweenEid] = duration;
|
|
@@ -188,7 +194,13 @@ function updateTweens(state: State, dt: number): void {
|
|
|
188
194
|
|
|
189
195
|
const elapsed = Tween.elapsed[tweenEid];
|
|
190
196
|
const duration = Tween.duration[tweenEid];
|
|
191
|
-
const rawProgress = duration
|
|
197
|
+
const rawProgress = duration <= 0 ? 1 : Math.min(elapsed / duration, 1);
|
|
198
|
+
|
|
199
|
+
if (!Number.isFinite(rawProgress)) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`Tween ${tweenEid} invalid progress: elapsed=${elapsed}, duration=${duration}, dt=${dt}`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
192
204
|
|
|
193
205
|
const easingFn = getEasing(Tween.easingIndex[tweenEid]);
|
|
194
206
|
const easedProgress = easingFn(rawProgress);
|
|
@@ -197,6 +209,12 @@ function updateTweens(state: State, dt: number): void {
|
|
|
197
209
|
const to = Tween.to[tweenEid];
|
|
198
210
|
const value = from + (to - from) * easedProgress;
|
|
199
211
|
|
|
212
|
+
if (!Number.isFinite(value)) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`Tween ${tweenEid} computed NaN: from=${from}, to=${to}, eased=${easedProgress}, raw=${rawProgress}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
200
218
|
if (binding && targetEid >= 0) {
|
|
201
219
|
binding.set(targetEid, value);
|
|
202
220
|
}
|