@multiplekex/shallot 0.2.4 → 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/arrows/index.ts +3 -3
- package/src/extras/caustic.ts +37 -0
- package/src/extras/gradient/index.ts +63 -69
- package/src/extras/index.ts +3 -0
- package/src/extras/lines/index.ts +3 -3
- package/src/extras/orbit/index.ts +1 -1
- package/src/extras/skylab/index.ts +314 -0
- package/src/extras/text/font.ts +69 -14
- package/src/extras/text/index.ts +17 -69
- package/src/extras/text/sdf.ts +13 -2
- package/src/extras/water/index.ts +119 -0
- package/src/standard/defaults.ts +2 -0
- package/src/standard/index.ts +2 -0
- package/src/standard/raster/batch.ts +149 -0
- package/src/standard/raster/forward.ts +832 -0
- package/src/standard/raster/index.ts +191 -0
- package/src/standard/raster/shadow.ts +408 -0
- package/src/standard/{render → raytracing}/bvh/blas.ts +336 -88
- package/src/standard/raytracing/bvh/radix.ts +473 -0
- package/src/standard/raytracing/bvh/refit.ts +711 -0
- package/src/standard/{render → raytracing}/bvh/structs.ts +0 -55
- package/src/standard/{render → raytracing}/bvh/tlas.ts +155 -140
- package/src/standard/{render → raytracing}/bvh/traverse.ts +72 -64
- package/src/standard/{render → raytracing}/depth.ts +9 -9
- package/src/standard/raytracing/index.ts +409 -0
- package/src/standard/{render → raytracing}/instance.ts +31 -16
- package/src/standard/{render → raytracing}/ray.ts +1 -1
- package/src/standard/raytracing/shaders.ts +798 -0
- package/src/standard/{render → raytracing}/triangle.ts +1 -1
- package/src/standard/render/camera.ts +96 -106
- package/src/standard/render/data.ts +1 -1
- package/src/standard/render/index.ts +136 -220
- package/src/standard/render/indirect.ts +9 -10
- package/src/standard/render/light.ts +2 -2
- package/src/standard/render/mesh.ts +404 -0
- package/src/standard/render/overlay.ts +8 -5
- package/src/standard/render/pass.ts +1 -1
- package/src/standard/render/postprocess.ts +263 -242
- package/src/standard/render/scene.ts +28 -16
- package/src/standard/render/surface/index.ts +81 -12
- package/src/standard/render/surface/shaders.ts +511 -0
- package/src/standard/render/surface/structs.ts +23 -6
- package/src/standard/tween/tween.ts +44 -115
- package/src/standard/render/bvh/radix.ts +0 -476
- package/src/standard/render/forward/index.ts +0 -259
- package/src/standard/render/forward/raster.ts +0 -228
- package/src/standard/render/mesh/box.ts +0 -20
- package/src/standard/render/mesh/index.ts +0 -446
- 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/shaders.ts +0 -484
- package/src/standard/render/surface/compile.ts +0 -67
- package/src/standard/render/surface/noise.ts +0 -45
- package/src/standard/render/surface/wgsl.ts +0 -573
- /package/src/standard/{render → raytracing}/intersection.ts +0 -0
|
@@ -28,6 +28,15 @@ struct Scene {
|
|
|
28
28
|
reflectionDepth: u32,
|
|
29
29
|
refractionDepth: u32,
|
|
30
30
|
instanceCount: u32,
|
|
31
|
+
time: f32,
|
|
32
|
+
_pad0: f32,
|
|
33
|
+
_pad1: f32,
|
|
34
|
+
_pad2: f32,
|
|
35
|
+
frustumPlanes: array<vec4<f32>, 6>,
|
|
36
|
+
}`;
|
|
37
|
+
|
|
38
|
+
export const SKY_STRUCT_WGSL = /* wgsl */ `
|
|
39
|
+
struct Sky {
|
|
31
40
|
hazeDensity: f32,
|
|
32
41
|
_pad2: f32,
|
|
33
42
|
_pad3: f32,
|
|
@@ -35,7 +44,6 @@ struct Scene {
|
|
|
35
44
|
hazeColor: vec4<f32>,
|
|
36
45
|
skyZenith: vec4<f32>,
|
|
37
46
|
skyHorizon: vec4<f32>,
|
|
38
|
-
frustumPlanes: array<vec4<f32>, 6>,
|
|
39
47
|
moonParams: vec4<f32>,
|
|
40
48
|
moonDirection: vec4<f32>,
|
|
41
49
|
starParams: vec4<f32>,
|
|
@@ -56,6 +64,16 @@ struct Data {
|
|
|
56
64
|
_pad2: u32,
|
|
57
65
|
}`;
|
|
58
66
|
|
|
67
|
+
export const SHADOW_STRUCT_WGSL = /* wgsl */ `
|
|
68
|
+
struct Shadow {
|
|
69
|
+
cascade0ViewProj: mat4x4<f32>,
|
|
70
|
+
cascade1ViewProj: mat4x4<f32>,
|
|
71
|
+
cascade2ViewProj: mat4x4<f32>,
|
|
72
|
+
cascade3ViewProj: mat4x4<f32>,
|
|
73
|
+
cascadeSplits: vec4<f32>,
|
|
74
|
+
cascadeTexelSizes: vec4<f32>,
|
|
75
|
+
}`;
|
|
76
|
+
|
|
59
77
|
export const WGSL_STRUCTS = /* wgsl */ `
|
|
60
78
|
struct VertexInput {
|
|
61
79
|
@location(0) position: vec3<f32>,
|
|
@@ -80,12 +98,11 @@ struct FragmentOutput {
|
|
|
80
98
|
|
|
81
99
|
${SCENE_STRUCT_WGSL}
|
|
82
100
|
|
|
101
|
+
${DATA_STRUCT_WGSL}
|
|
102
|
+
|
|
83
103
|
@group(0) @binding(0) var<uniform> scene: Scene;
|
|
84
104
|
@group(0) @binding(1) var<storage, read> entityIds: array<u32>;
|
|
85
105
|
@group(0) @binding(2) var<storage, read> matrices: array<mat4x4<f32>>;
|
|
86
|
-
@group(0) @binding(3) var<storage, read>
|
|
87
|
-
@group(0) @binding(4) var<storage, read>
|
|
88
|
-
@group(0) @binding(5) var<storage, read> pbr: array<vec4<f32>>;
|
|
89
|
-
@group(0) @binding(6) var<storage, read> emission: array<vec4<f32>>;
|
|
90
|
-
@group(0) @binding(7) var<storage, read> shapes: array<u32>;
|
|
106
|
+
@group(0) @binding(3) var<storage, read> sizes: array<vec4<f32>>;
|
|
107
|
+
@group(0) @binding(4) var<storage, read> data: array<Data>;
|
|
91
108
|
`;
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
defineRelation,
|
|
3
|
-
registerPostLoadHook,
|
|
4
|
-
toCamelCase,
|
|
5
|
-
type State,
|
|
6
|
-
type System,
|
|
7
|
-
type Plugin,
|
|
8
|
-
type PostLoadContext,
|
|
9
|
-
} from "../../core";
|
|
1
|
+
import { defineRelation, toCamelCase, type State, type System, type Plugin } from "../../core";
|
|
10
2
|
import {
|
|
11
3
|
setTraits,
|
|
12
4
|
getRegisteredComponent,
|
|
@@ -63,23 +55,17 @@ function bindFieldAccessor(
|
|
|
63
55
|
return accessor;
|
|
64
56
|
}
|
|
65
57
|
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
function getOrBindAccessor(tweenEid: number): FieldAccessor | undefined {
|
|
59
|
+
const existing = fieldAccessors.get(tweenEid);
|
|
60
|
+
if (existing) return existing;
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const value = part.slice(colonIdx + 1).trim();
|
|
78
|
-
if (key && value) parsed[key] = value;
|
|
79
|
-
}
|
|
80
|
-
return parsed;
|
|
81
|
-
}
|
|
82
|
-
return attrs;
|
|
62
|
+
const path = Tween.field[tweenEid];
|
|
63
|
+
if (!path) return undefined;
|
|
64
|
+
|
|
65
|
+
const parsed = resolveFieldPath(path);
|
|
66
|
+
if (!parsed) return undefined;
|
|
67
|
+
|
|
68
|
+
return bindFieldAccessor(tweenEid, parsed.component, parsed.field) ?? undefined;
|
|
83
69
|
}
|
|
84
70
|
|
|
85
71
|
export const TweenState = {
|
|
@@ -88,6 +74,29 @@ export const TweenState = {
|
|
|
88
74
|
COMPLETE: 2,
|
|
89
75
|
} as const;
|
|
90
76
|
|
|
77
|
+
const fieldPaths = new Map<number, string>();
|
|
78
|
+
|
|
79
|
+
function fieldProxy(): Record<number, string | undefined> {
|
|
80
|
+
return new Proxy({} as Record<number, string | undefined>, {
|
|
81
|
+
get(_, prop) {
|
|
82
|
+
const eid = Number(prop);
|
|
83
|
+
if (Number.isNaN(eid)) return undefined;
|
|
84
|
+
return fieldPaths.get(eid);
|
|
85
|
+
},
|
|
86
|
+
set(_, prop, value) {
|
|
87
|
+
const eid = Number(prop);
|
|
88
|
+
if (Number.isNaN(eid)) return false;
|
|
89
|
+
if (value === undefined || value === null) {
|
|
90
|
+
fieldPaths.delete(eid);
|
|
91
|
+
} else {
|
|
92
|
+
fieldPaths.set(eid, value as string);
|
|
93
|
+
}
|
|
94
|
+
fieldAccessors.delete(eid);
|
|
95
|
+
return true;
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
91
100
|
export const Tween = {
|
|
92
101
|
state: [] as number[],
|
|
93
102
|
from: [] as number[],
|
|
@@ -95,7 +104,8 @@ export const Tween = {
|
|
|
95
104
|
duration: [] as number[],
|
|
96
105
|
elapsed: [] as number[],
|
|
97
106
|
delay: [] as number[],
|
|
98
|
-
|
|
107
|
+
easing: [] as number[],
|
|
108
|
+
field: fieldProxy(),
|
|
99
109
|
};
|
|
100
110
|
|
|
101
111
|
setTraits(Tween, {
|
|
@@ -106,96 +116,18 @@ setTraits(Tween, {
|
|
|
106
116
|
duration: 1,
|
|
107
117
|
elapsed: 0,
|
|
108
118
|
delay: 0,
|
|
109
|
-
|
|
119
|
+
easing: 0,
|
|
110
120
|
}),
|
|
111
|
-
|
|
112
|
-
const parsed = parseTweenAttrs(attrs);
|
|
113
|
-
const result: Record<string, number> = {};
|
|
114
|
-
|
|
115
|
-
if (parsed.duration) result.duration = parseFloat(parsed.duration);
|
|
116
|
-
if (parsed.delay) result.delay = parseFloat(parsed.delay);
|
|
117
|
-
if (parsed.easing) result.easingIndex = getEasingIndex(parsed.easing);
|
|
118
|
-
|
|
119
|
-
if (parsed.target) {
|
|
120
|
-
setupTweenFromXml(parsed, eid);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return result;
|
|
124
|
-
},
|
|
121
|
+
parse: { easing: getEasingIndex },
|
|
125
122
|
});
|
|
126
123
|
|
|
127
|
-
export const TweenTarget = defineRelation("
|
|
124
|
+
export const TweenTarget = defineRelation("target", {
|
|
128
125
|
exclusive: true,
|
|
129
126
|
});
|
|
130
127
|
|
|
131
|
-
interface ParsedTargetPath {
|
|
132
|
-
readonly entity: string;
|
|
133
|
-
readonly component: string;
|
|
134
|
-
readonly field: string;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function parseTargetPath(path: string): ParsedTargetPath | null {
|
|
138
|
-
if (!path.startsWith("@")) return null;
|
|
139
|
-
|
|
140
|
-
const rest = path.slice(1);
|
|
141
|
-
const firstDot = rest.indexOf(".");
|
|
142
|
-
if (firstDot === -1) return null;
|
|
143
|
-
|
|
144
|
-
const entity = rest.slice(0, firstDot);
|
|
145
|
-
const fieldPath = rest.slice(firstDot + 1);
|
|
146
|
-
const dotIndex = fieldPath.lastIndexOf(".");
|
|
147
|
-
if (dotIndex === -1) return null;
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
entity,
|
|
151
|
-
component: fieldPath.slice(0, dotIndex),
|
|
152
|
-
field: fieldPath.slice(dotIndex + 1),
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
interface PendingTween {
|
|
157
|
-
readonly tweenEid: number;
|
|
158
|
-
readonly target: string;
|
|
159
|
-
readonly to: string;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
let pendingXmlTweens: PendingTween[] = [];
|
|
163
|
-
|
|
164
|
-
function setupTweenFromXml(attrs: Record<string, string>, tweenEid: number): void {
|
|
165
|
-
pendingXmlTweens.push({
|
|
166
|
-
tweenEid,
|
|
167
|
-
target: attrs.target,
|
|
168
|
-
to: attrs.to,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export function finalizePendingTweens(state: State, context: PostLoadContext): void {
|
|
173
|
-
for (const pending of pendingXmlTweens) {
|
|
174
|
-
const parsed = parseTargetPath(pending.target);
|
|
175
|
-
if (!parsed) continue;
|
|
176
|
-
|
|
177
|
-
const targetEid = context.getEntityByName(parsed.entity);
|
|
178
|
-
if (targetEid === null) continue;
|
|
179
|
-
|
|
180
|
-
const binding = bindFieldAccessor(pending.tweenEid, parsed.component, parsed.field);
|
|
181
|
-
if (!binding) continue;
|
|
182
|
-
|
|
183
|
-
state.addRelation(pending.tweenEid, TweenTarget, targetEid);
|
|
184
|
-
const toValue =
|
|
185
|
-
pending.to.startsWith("0x") || pending.to.startsWith("0X")
|
|
186
|
-
? parseInt(pending.to, 16)
|
|
187
|
-
: parseFloat(pending.to);
|
|
188
|
-
if (!Number.isFinite(toValue)) {
|
|
189
|
-
throw new Error(`Tween has invalid 'to' value: "${pending.to}" (parsed as ${toValue})`);
|
|
190
|
-
}
|
|
191
|
-
Tween.to[pending.tweenEid] = toValue;
|
|
192
|
-
}
|
|
193
|
-
pendingXmlTweens = [];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
128
|
export function captureFromValue(state: State, tweenEid: number): void {
|
|
197
129
|
const targetEid = state.getFirstRelationTarget(tweenEid, TweenTarget);
|
|
198
|
-
const binding =
|
|
130
|
+
const binding = getOrBindAccessor(tweenEid);
|
|
199
131
|
|
|
200
132
|
if (binding && targetEid >= 0) {
|
|
201
133
|
Tween.from[tweenEid] = binding.get(targetEid) ?? 0;
|
|
@@ -209,7 +141,7 @@ export function ensureResolved(state: State, tweenEid: number): void {
|
|
|
209
141
|
if (duration > 0 && elapsed >= duration) return;
|
|
210
142
|
|
|
211
143
|
const targetEid = state.getFirstRelationTarget(tweenEid, TweenTarget);
|
|
212
|
-
const binding =
|
|
144
|
+
const binding = getOrBindAccessor(tweenEid);
|
|
213
145
|
|
|
214
146
|
if (binding && targetEid >= 0) {
|
|
215
147
|
const toValue = Tween.to[tweenEid];
|
|
@@ -235,7 +167,7 @@ function updateTweens(state: State, dt: number): void {
|
|
|
235
167
|
if (tweenState !== TweenState.PLAYING) continue;
|
|
236
168
|
|
|
237
169
|
const targetEid = state.getFirstRelationTarget(tweenEid, TweenTarget);
|
|
238
|
-
const binding =
|
|
170
|
+
const binding = getOrBindAccessor(tweenEid);
|
|
239
171
|
|
|
240
172
|
if (Tween.elapsed[tweenEid] === 0 && binding && targetEid >= 0) {
|
|
241
173
|
Tween.from[tweenEid] = binding.get(targetEid) ?? 0;
|
|
@@ -253,7 +185,7 @@ function updateTweens(state: State, dt: number): void {
|
|
|
253
185
|
);
|
|
254
186
|
}
|
|
255
187
|
|
|
256
|
-
const easingFn = getEasing(Tween.
|
|
188
|
+
const easingFn = getEasing(Tween.easing[tweenEid]);
|
|
257
189
|
const easedProgress = easingFn(rawProgress);
|
|
258
190
|
|
|
259
191
|
const from = Tween.from[tweenEid];
|
|
@@ -305,7 +237,7 @@ export function createTween(
|
|
|
305
237
|
Tween.to[tweenEid] = options.to;
|
|
306
238
|
Tween.duration[tweenEid] = options.duration ?? 1;
|
|
307
239
|
Tween.elapsed[tweenEid] = 0;
|
|
308
|
-
Tween.
|
|
240
|
+
Tween.easing[tweenEid] = getEasingIndex(options.easing ?? "linear");
|
|
309
241
|
|
|
310
242
|
return tweenEid;
|
|
311
243
|
}
|
|
@@ -327,7 +259,4 @@ export const TweenPlugin: Plugin = {
|
|
|
327
259
|
systems: [TweenSystem],
|
|
328
260
|
components: { Tween, Sequence, Pause },
|
|
329
261
|
relations: [TweenTarget],
|
|
330
|
-
initialize() {
|
|
331
|
-
registerPostLoadHook(finalizePendingTweens);
|
|
332
|
-
},
|
|
333
262
|
};
|