@multiplekex/shallot 0.1.12 → 0.2.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.
Files changed (62) hide show
  1. package/package.json +3 -4
  2. package/src/core/builder.ts +71 -32
  3. package/src/core/component.ts +25 -11
  4. package/src/core/index.ts +14 -13
  5. package/src/core/math.ts +135 -0
  6. package/src/core/runtime.ts +0 -1
  7. package/src/core/state.ts +9 -68
  8. package/src/core/xml.ts +381 -265
  9. package/src/editor/format.ts +5 -0
  10. package/src/editor/index.ts +101 -0
  11. package/src/extras/arrows/index.ts +28 -69
  12. package/src/extras/gradient/index.ts +36 -52
  13. package/src/extras/lines/index.ts +51 -122
  14. package/src/extras/orbit/index.ts +40 -15
  15. package/src/extras/text/font.ts +546 -0
  16. package/src/extras/text/index.ts +158 -204
  17. package/src/extras/text/sdf.ts +429 -0
  18. package/src/standard/activity/index.ts +172 -0
  19. package/src/standard/compute/graph.ts +23 -23
  20. package/src/standard/compute/index.ts +76 -61
  21. package/src/standard/defaults.ts +8 -5
  22. package/src/standard/index.ts +1 -0
  23. package/src/standard/input/index.ts +30 -19
  24. package/src/standard/loading/index.ts +18 -13
  25. package/src/standard/render/bvh/blas.ts +752 -0
  26. package/src/standard/render/bvh/radix.ts +476 -0
  27. package/src/standard/render/bvh/structs.ts +167 -0
  28. package/src/standard/render/bvh/tlas.ts +886 -0
  29. package/src/standard/render/bvh/traverse.ts +467 -0
  30. package/src/standard/render/camera.ts +302 -27
  31. package/src/standard/render/data.ts +93 -0
  32. package/src/standard/render/depth.ts +117 -0
  33. package/src/standard/render/forward/index.ts +259 -0
  34. package/src/standard/render/forward/raster.ts +228 -0
  35. package/src/standard/render/index.ts +443 -70
  36. package/src/standard/render/indirect.ts +40 -0
  37. package/src/standard/render/instance.ts +214 -0
  38. package/src/standard/render/intersection.ts +72 -0
  39. package/src/standard/render/light.ts +16 -16
  40. package/src/standard/render/mesh/index.ts +67 -75
  41. package/src/standard/render/mesh/unified.ts +96 -0
  42. package/src/standard/render/{transparent.ts → overlay.ts} +14 -15
  43. package/src/standard/render/pass.ts +10 -4
  44. package/src/standard/render/postprocess.ts +142 -64
  45. package/src/standard/render/ray.ts +61 -0
  46. package/src/standard/render/scene.ts +38 -164
  47. package/src/standard/render/shaders.ts +484 -0
  48. package/src/standard/render/surface/compile.ts +3 -10
  49. package/src/standard/render/surface/index.ts +60 -30
  50. package/src/standard/render/surface/noise.ts +45 -0
  51. package/src/standard/render/surface/structs.ts +60 -19
  52. package/src/standard/render/surface/wgsl.ts +573 -0
  53. package/src/standard/render/triangle.ts +84 -0
  54. package/src/standard/transforms/index.ts +4 -6
  55. package/src/standard/tween/index.ts +10 -1
  56. package/src/standard/tween/sequence.ts +24 -16
  57. package/src/standard/tween/tween.ts +67 -16
  58. package/src/core/types.ts +0 -37
  59. package/src/standard/compute/inspect.ts +0 -201
  60. package/src/standard/compute/pass.ts +0 -23
  61. package/src/standard/compute/timing.ts +0 -139
  62. package/src/standard/render/forward.ts +0 -273
@@ -1,71 +1,48 @@
1
- import { MAX_ENTITIES, resource, type Plugin, type State, type System } from "../../core";
2
- import { setTraits, type FieldAccessor } from "../../core/component";
1
+ import {
2
+ MAX_ENTITIES,
3
+ resource,
4
+ createFieldProxy,
5
+ type Plugin,
6
+ type State,
7
+ type System,
8
+ type FieldProxy,
9
+ } from "../../core";
10
+ import { setTraits } from "../../core/component";
3
11
  import { Compute, ComputePlugin, createEntityIdBuffer } from "../../standard/compute";
4
12
  import {
5
13
  Render,
6
14
  RenderPlugin,
7
- DEPTH_FORMAT,
8
15
  Pass,
9
16
  registerDraw,
10
17
  type Draw,
11
18
  type SharedPassContext,
12
19
  } from "../../standard/render";
20
+ import { DEPTH_FORMAT } from "../../standard/render/scene";
21
+ import { SCENE_STRUCT_WGSL } from "../../standard/render/shaders";
13
22
  import { Transform } from "../../standard/transforms";
14
23
 
15
24
  export const LineData = {
16
25
  data: new Float32Array(MAX_ENTITIES * 12),
17
26
  };
18
27
 
19
- interface LineProxy extends Array<number>, FieldAccessor {}
20
-
21
- function lineProxy(offset: number): LineProxy {
22
- const data = LineData.data;
23
-
24
- function getValue(eid: number): number {
25
- return data[eid * 12 + offset];
26
- }
27
-
28
- function setValue(eid: number, value: number): void {
29
- data[eid * 12 + offset] = value;
30
- }
31
-
32
- return new Proxy([] as unknown as LineProxy, {
33
- get(_, prop) {
34
- if (prop === "get") return getValue;
35
- if (prop === "set") return setValue;
36
- const eid = Number(prop);
37
- if (Number.isNaN(eid)) return undefined;
38
- return getValue(eid);
39
- },
40
- set(_, prop, value) {
41
- const eid = Number(prop);
42
- if (Number.isNaN(eid)) return false;
43
- setValue(eid, value);
44
- return true;
45
- },
46
- });
47
- }
48
-
49
- function colorProxy(): LineProxy {
50
- const data = LineData.data;
51
-
28
+ function packedColorProxy(data: Float32Array, stride: number, offset: number): FieldProxy {
52
29
  function getValue(eid: number): number {
53
- const offset = eid * 12 + 8;
54
- const r = Math.round(data[offset] * 255);
55
- const g = Math.round(data[offset + 1] * 255);
56
- const b = Math.round(data[offset + 2] * 255);
30
+ const o = eid * stride + offset;
31
+ const r = Math.round(data[o] * 255);
32
+ const g = Math.round(data[o + 1] * 255);
33
+ const b = Math.round(data[o + 2] * 255);
57
34
  return (r << 16) | (g << 8) | b;
58
35
  }
59
36
 
60
37
  function setValue(eid: number, value: number): void {
61
- const offset = eid * 12 + 8;
62
- data[offset] = ((value >> 16) & 0xff) / 255;
63
- data[offset + 1] = ((value >> 8) & 0xff) / 255;
64
- data[offset + 2] = (value & 0xff) / 255;
65
- data[offset + 3] = 1;
38
+ const o = eid * stride + offset;
39
+ data[o] = ((value >> 16) & 0xff) / 255;
40
+ data[o + 1] = ((value >> 8) & 0xff) / 255;
41
+ data[o + 2] = (value & 0xff) / 255;
42
+ data[o + 3] = 1;
66
43
  }
67
44
 
68
- return new Proxy([] as unknown as LineProxy, {
45
+ return new Proxy([] as unknown as FieldProxy, {
69
46
  get(_, prop) {
70
47
  if (prop === "get") return getValue;
71
48
  if (prop === "set") return setValue;
@@ -82,56 +59,30 @@ function colorProxy(): LineProxy {
82
59
  });
83
60
  }
84
61
 
85
- function colorChannelProxy(channelIndex: number): LineProxy {
86
- const data = LineData.data;
87
-
88
- function getValue(eid: number): number {
89
- return data[eid * 12 + 8 + channelIndex];
90
- }
91
-
92
- function setValue(eid: number, value: number): void {
93
- data[eid * 12 + 8 + channelIndex] = value;
94
- }
95
-
96
- return new Proxy([] as unknown as LineProxy, {
97
- get(_, prop) {
98
- if (prop === "get") return getValue;
99
- if (prop === "set") return setValue;
100
- const eid = Number(prop);
101
- if (Number.isNaN(eid)) return undefined;
102
- return getValue(eid);
103
- },
104
- set(_, prop, value) {
105
- const eid = Number(prop);
106
- if (Number.isNaN(eid)) return false;
107
- setValue(eid, value);
108
- return true;
109
- },
110
- });
111
- }
62
+ const data = LineData.data;
112
63
 
113
64
  export const Line: {
114
- offsetX: LineProxy;
115
- offsetY: LineProxy;
116
- offsetZ: LineProxy;
117
- thickness: LineProxy;
118
- visible: LineProxy;
119
- opacity: LineProxy;
120
- color: LineProxy;
121
- colorR: LineProxy;
122
- colorG: LineProxy;
123
- colorB: LineProxy;
65
+ offsetX: FieldProxy;
66
+ offsetY: FieldProxy;
67
+ offsetZ: FieldProxy;
68
+ thickness: FieldProxy;
69
+ visible: FieldProxy;
70
+ opacity: FieldProxy;
71
+ color: FieldProxy;
72
+ colorR: FieldProxy;
73
+ colorG: FieldProxy;
74
+ colorB: FieldProxy;
124
75
  } = {
125
- offsetX: lineProxy(0),
126
- offsetY: lineProxy(1),
127
- offsetZ: lineProxy(2),
128
- thickness: lineProxy(3),
129
- visible: lineProxy(4),
130
- opacity: lineProxy(7),
131
- color: colorProxy(),
132
- colorR: colorChannelProxy(0),
133
- colorG: colorChannelProxy(1),
134
- colorB: colorChannelProxy(2),
76
+ offsetX: createFieldProxy(data, 12, 0),
77
+ offsetY: createFieldProxy(data, 12, 1),
78
+ offsetZ: createFieldProxy(data, 12, 2),
79
+ thickness: createFieldProxy(data, 12, 3),
80
+ visible: createFieldProxy(data, 12, 4),
81
+ opacity: createFieldProxy(data, 12, 7),
82
+ color: packedColorProxy(data, 12, 8),
83
+ colorR: createFieldProxy(data, 12, 8),
84
+ colorG: createFieldProxy(data, 12, 9),
85
+ colorB: createFieldProxy(data, 12, 10),
135
86
  };
136
87
 
137
88
  setTraits(Line, {
@@ -144,18 +95,6 @@ setTraits(Line, {
144
95
  opacity: 1,
145
96
  color: 0xffffff,
146
97
  }),
147
- accessors: {
148
- offsetX: Line.offsetX,
149
- offsetY: Line.offsetY,
150
- offsetZ: Line.offsetZ,
151
- thickness: Line.thickness,
152
- visible: Line.visible,
153
- opacity: Line.opacity,
154
- color: Line.color,
155
- colorR: Line.colorR,
156
- colorG: Line.colorG,
157
- colorB: Line.colorB,
158
- },
159
98
  });
160
99
 
161
100
  export interface LinesConfig {
@@ -174,16 +113,7 @@ struct VertexOutput {
174
113
  @location(2) halfWidth: f32,
175
114
  }
176
115
 
177
- struct Scene {
178
- viewProj: mat4x4<f32>,
179
- cameraWorld: mat4x4<f32>,
180
- ambientColor: vec4<f32>,
181
- sunDirection: vec4<f32>,
182
- sunColor: vec4<f32>,
183
- cameraMode: f32,
184
- cameraSize: f32,
185
- viewport: vec2<f32>,
186
- }
116
+ ${SCENE_STRUCT_WGSL}
187
117
 
188
118
  struct LineData {
189
119
  offset: vec3<f32>,
@@ -220,8 +150,8 @@ fn vs(@builtin(vertex_index) vid: u32, @builtin(instance_index) iid: u32) -> Ver
220
150
  let len = length(dir);
221
151
  let normDir = select(vec2(1.0, 0.0), dir / len, len > 0.0001);
222
152
 
223
- // Convert thickness from pixels to NDC (2 pixels / viewport = NDC units)
224
- let halfWidth = line.thickness * 0.5;
153
+ let scale = scene.viewport.y / 1080.0;
154
+ let halfWidth = line.thickness * 0.5 * scale;
225
155
  let aaPadding = 1.0;
226
156
  let totalHalf = halfWidth + aaPadding;
227
157
  let perpNDC = vec2(-normDir.y, normDir.x) * totalHalf * 2.0 / scene.viewport;
@@ -241,7 +171,6 @@ fn vs(@builtin(vertex_index) vid: u32, @builtin(instance_index) iid: u32) -> Ver
241
171
 
242
172
  let depth = mix(startClip.z / startClip.w, endClip.z / endClip.w, t);
243
173
 
244
- // edge is -1 to +1 across quad, convert to pixel distance from line center
245
174
  let pixelDist = edge * totalHalf;
246
175
 
247
176
  var out: VertexOutput;
@@ -323,7 +252,7 @@ function createLinesDraw(config: LinesConfig): Draw {
323
252
 
324
253
  return {
325
254
  id: "lines",
326
- pass: Pass.Transparent,
255
+ pass: Pass.Overlay,
327
256
  order: 0,
328
257
 
329
258
  execute() {},
@@ -355,13 +284,13 @@ function createLinesDraw(config: LinesConfig): Draw {
355
284
  };
356
285
  }
357
286
 
358
- export interface LinesState {
287
+ export interface Lines {
359
288
  buffer: GPUBuffer;
360
289
  entityIds: GPUBuffer;
361
290
  count: number;
362
291
  }
363
292
 
364
- export const Lines = resource<LinesState>("lines");
293
+ export const Lines = resource<Lines>("lines");
365
294
 
366
295
  const entityIdArray = new Uint32Array(MAX_ENTITIES);
367
296
 
@@ -400,7 +329,7 @@ export const LinesPlugin: Plugin = {
400
329
 
401
330
  const { device } = compute;
402
331
 
403
- const linesState: LinesState = {
332
+ const linesState: Lines = {
404
333
  buffer: device.createBuffer({
405
334
  label: "lines",
406
335
  size: MAX_ENTITIES * 12 * 4,
@@ -1,7 +1,8 @@
1
1
  import { setTraits } from "../../core/component";
2
- import { clamp, lookAt, type State, type System, type Plugin, type MouseState } from "../../core";
2
+ import { clamp, lookAt, type State, type System, type Plugin } from "../../core";
3
3
  import { Transform } from "../../standard/transforms";
4
- import { Input, InputPlugin } from "../../standard/input";
4
+ import { Inputs, InputPlugin, type Mouse } from "../../standard/input";
5
+ import { Camera, CameraMode } from "../../standard/render/camera";
5
6
 
6
7
  const Tau = Math.PI * 2;
7
8
 
@@ -23,6 +24,9 @@ export const Orbit = {
23
24
  maxPitch: [] as number[],
24
25
  minDistance: [] as number[],
25
26
  maxDistance: [] as number[],
27
+ minSize: [] as number[],
28
+ maxSize: [] as number[],
29
+ targetSize: [] as number[],
26
30
  smoothness: [] as number[],
27
31
  sensitivity: [] as number[],
28
32
  zoomSpeed: [] as number[],
@@ -42,6 +46,9 @@ setTraits(Orbit, {
42
46
  maxPitch: Math.PI / 2 - 0.01,
43
47
  minDistance: 1,
44
48
  maxDistance: 25,
49
+ minSize: 0.5,
50
+ maxSize: 50,
51
+ targetSize: 5,
45
52
  smoothness: 0.3,
46
53
  sensitivity: 0.005,
47
54
  zoomSpeed: 0.025,
@@ -63,7 +70,7 @@ function angleDiff(from: number, to: number): number {
63
70
  return diff > Math.PI ? diff - Tau : diff;
64
71
  }
65
72
 
66
- function isOrbitButton(mouse: Readonly<MouseState>, button: number): boolean {
73
+ function isOrbitButton(mouse: Readonly<Mouse>, button: number): boolean {
67
74
  if (button === OrbitButton.Left) return mouse.left;
68
75
  if (button === OrbitButton.Middle) return mouse.middle;
69
76
  return mouse.right;
@@ -73,7 +80,7 @@ export const OrbitSystem: System = {
73
80
  group: "simulation",
74
81
 
75
82
  update(state: State) {
76
- const input = Input.from(state);
83
+ const input = Inputs.from(state);
77
84
  const dt = state.time.deltaTime;
78
85
 
79
86
  for (const eid of state.query([Orbit, Transform])) {
@@ -81,10 +88,11 @@ export const OrbitSystem: System = {
81
88
  const zoomSpeed = Orbit.zoomSpeed[eid];
82
89
  const minPitch = Orbit.minPitch[eid];
83
90
  const maxPitch = Orbit.maxPitch[eid];
84
- const minDistance = Orbit.minDistance[eid];
85
- const maxDistance = Orbit.maxDistance[eid];
86
91
  const smoothness = Orbit.smoothness[eid];
87
92
 
93
+ const hasCamera = state.hasComponent(eid, Camera);
94
+ const isOrtho = hasCamera && Camera.mode[eid] === CameraMode.Orthographic;
95
+
88
96
  if (input && isOrbitButton(input.mouse, Orbit.button[eid])) {
89
97
  Orbit.targetYaw[eid] -= input.mouse.deltaX * sensitivity;
90
98
  Orbit.targetPitch[eid] = clamp(
@@ -94,15 +102,26 @@ export const OrbitSystem: System = {
94
102
  );
95
103
  }
96
104
 
97
- if (input && input.mouse.scrollDelta !== 0) {
98
- const currentDistance = Orbit.targetDistance[eid];
99
- const distanceScale = Math.max(0.3, currentDistance * 0.08);
100
- const zoomDelta = input.mouse.scrollDelta * zoomSpeed * distanceScale;
101
- Orbit.targetDistance[eid] = clamp(
102
- currentDistance + zoomDelta,
103
- minDistance,
104
- maxDistance
105
- );
105
+ if (input && input.mouse.scroll !== 0) {
106
+ if (isOrtho) {
107
+ const currentSize = Orbit.targetSize[eid];
108
+ const sizeScale = Math.max(0.1, currentSize * 0.08);
109
+ const zoomDelta = input.mouse.scroll * zoomSpeed * sizeScale;
110
+ Orbit.targetSize[eid] = clamp(
111
+ currentSize + zoomDelta,
112
+ Orbit.minSize[eid],
113
+ Orbit.maxSize[eid]
114
+ );
115
+ } else {
116
+ const currentDistance = Orbit.targetDistance[eid];
117
+ const distanceScale = Math.max(0.3, currentDistance * 0.08);
118
+ const zoomDelta = input.mouse.scroll * zoomSpeed * distanceScale;
119
+ Orbit.targetDistance[eid] = clamp(
120
+ currentDistance + zoomDelta,
121
+ Orbit.minDistance[eid],
122
+ Orbit.maxDistance[eid]
123
+ );
124
+ }
106
125
  }
107
126
 
108
127
  const t = smoothLerp(smoothness, dt);
@@ -110,6 +129,12 @@ export const OrbitSystem: System = {
110
129
  Orbit.pitch[eid] += (Orbit.targetPitch[eid] - Orbit.pitch[eid]) * t;
111
130
  Orbit.distance[eid] += (Orbit.targetDistance[eid] - Orbit.distance[eid]) * t;
112
131
 
132
+ if (isOrtho) {
133
+ const currentSize = Camera.size[eid];
134
+ const targetSize = Orbit.targetSize[eid];
135
+ Camera.size[eid] = currentSize + (targetSize - currentSize) * t;
136
+ }
137
+
113
138
  const yaw = Orbit.yaw[eid];
114
139
  const pitch = Orbit.pitch[eid];
115
140
  const distance = Orbit.distance[eid];