@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.
Files changed (41) hide show
  1. package/package.json +1 -1
  2. package/src/core/component.ts +1 -1
  3. package/src/core/index.ts +1 -13
  4. package/src/core/math.ts +186 -0
  5. package/src/core/state.ts +1 -1
  6. package/src/core/xml.ts +56 -41
  7. package/src/extras/orbit/index.ts +1 -1
  8. package/src/extras/text/index.ts +10 -65
  9. package/src/extras/{water.ts → water/index.ts} +59 -4
  10. package/src/standard/raster/batch.ts +149 -0
  11. package/src/standard/raster/forward.ts +832 -0
  12. package/src/standard/raster/index.ts +146 -472
  13. package/src/standard/raster/shadow.ts +408 -0
  14. package/src/standard/raytracing/bvh/blas.ts +335 -87
  15. package/src/standard/raytracing/bvh/radix.ts +225 -228
  16. package/src/standard/raytracing/bvh/refit.ts +711 -0
  17. package/src/standard/raytracing/bvh/structs.ts +0 -55
  18. package/src/standard/raytracing/bvh/tlas.ts +153 -141
  19. package/src/standard/raytracing/bvh/traverse.ts +72 -64
  20. package/src/standard/raytracing/index.ts +233 -204
  21. package/src/standard/raytracing/instance.ts +30 -18
  22. package/src/standard/raytracing/ray.ts +1 -1
  23. package/src/standard/raytracing/shaders.ts +23 -40
  24. package/src/standard/render/camera.ts +10 -28
  25. package/src/standard/render/data.ts +1 -1
  26. package/src/standard/render/index.ts +68 -12
  27. package/src/standard/render/light.ts +2 -2
  28. package/src/standard/render/mesh.ts +404 -0
  29. package/src/standard/render/overlay.ts +5 -2
  30. package/src/standard/render/postprocess.ts +263 -267
  31. package/src/standard/render/surface/index.ts +81 -12
  32. package/src/standard/render/surface/shaders.ts +265 -11
  33. package/src/standard/render/surface/structs.ts +10 -0
  34. package/src/standard/tween/tween.ts +44 -115
  35. package/src/standard/render/mesh/box.ts +0 -20
  36. package/src/standard/render/mesh/index.ts +0 -315
  37. package/src/standard/render/mesh/plane.ts +0 -11
  38. package/src/standard/render/mesh/sphere.ts +0 -40
  39. package/src/standard/render/mesh/unified.ts +0 -96
  40. package/src/standard/render/surface/compile.ts +0 -65
  41. package/src/standard/render/surface/noise.ts +0 -58
@@ -1,7 +1,7 @@
1
1
  import type { SurfaceData } from "../render/surface";
2
2
  import {
3
3
  compileVertexBody,
4
- WGSL_LIGHTING_CALC,
4
+ SPECULAR_WGSL,
5
5
  SKY_DIR_WGSL,
6
6
  HAZE_WGSL,
7
7
  SKY_WGSL,
@@ -22,26 +22,6 @@ import {
22
22
  DATA_STRUCT_WGSL,
23
23
  } from "../render/surface/structs";
24
24
 
25
- const SPECULAR_WGSL = /* wgsl */ `
26
- const DIELECTRIC_F0: f32 = 0.04;
27
-
28
- fn blinnPhongSpecular(N: vec3<f32>, L: vec3<f32>, V: vec3<f32>, roughness: f32) -> f32 {
29
- let H = normalize(L + V);
30
- let NdotH = max(dot(N, H), 0.0);
31
- let shininess = pow(2.0, (1.0 - roughness) * 10.0);
32
- let intensity = (1.0 - roughness) * (1.0 - roughness);
33
- return pow(NdotH, shininess) * intensity;
34
- }
35
-
36
- fn schlickFresnel(cosTheta: f32, F0: vec3<f32>) -> vec3<f32> {
37
- return F0 + (vec3(1.0) - F0) * pow(1.0 - cosTheta, 5.0);
38
- }
39
-
40
- fn computeF0Vec(baseColor: vec3<f32>, metallic: f32) -> vec3<f32> {
41
- return mix(vec3(DIELECTRIC_F0), baseColor, metallic);
42
- }
43
- `;
44
-
45
25
  const REFRACTION_WGSL = /* wgsl */ `
46
26
  fn refractRay(I: vec3<f32>, N: vec3<f32>, eta: f32) -> vec4<f32> {
47
27
  let cosI = -dot(I, N);
@@ -140,13 +120,8 @@ fn sampleSoftShadow(origin: vec3<f32>, sunDir: vec3<f32>, softness: f32, samples
140
120
  }
141
121
  `;
142
122
 
143
- function compileApplyLighting(lit?: boolean, shadows?: boolean, reflections?: boolean): string {
144
- if (lit === false) {
145
- return "return surface.baseColor;";
146
- }
147
-
123
+ function compileApplyLighting(shadows?: boolean, reflections?: boolean): string {
148
124
  if (shadows && reflections) {
149
- // shadowFactor is now passed as parameter, computed once in main kernel
150
125
  return `
151
126
  let V = -rayDir;
152
127
  let L = -scene.sunDirection.xyz;
@@ -186,7 +161,7 @@ function compileApplyLighting(lit?: boolean, shadows?: boolean, reflections?: bo
186
161
 
187
162
  let F0 = computeF0Vec(surface.baseColor, surface.metallic);
188
163
  let F = schlickFresnel(NdotV, F0);
189
- let diffuseWeight = (1.0 - surface.metallic) * (vec3(1.0) - F);
164
+ let diffuseWeight = 1.0 - surface.metallic;
190
165
  let ambient = scene.ambientColor.rgb * scene.ambientColor.a;
191
166
  let sunDiffuse = scene.sunColor.rgb * NdotL * shadowFactor;
192
167
  let diffuseColor = surface.baseColor * (ambient + sunDiffuse) * diffuseWeight + surface.emission;
@@ -226,13 +201,25 @@ function compileApplyLighting(lit?: boolean, shadows?: boolean, reflections?: bo
226
201
  return finalColor;`;
227
202
  }
228
203
 
229
- return `${WGSL_LIGHTING_CALC}
230
- return surface.baseColor * lighting + surface.emission;`;
204
+ return `
205
+ let V = -rayDir;
206
+ let L = -scene.sunDirection.xyz;
207
+ let NdotL = max(dot(surface.normal, L), 0.0);
208
+ let NdotV = max(dot(surface.normal, V), 0.0);
209
+ let F0 = computeF0Vec(surface.baseColor, surface.metallic);
210
+ let F = schlickFresnel(NdotV, F0);
211
+ let diffuseWeight = 1.0 - surface.metallic;
212
+ let ambient = scene.ambientColor.rgb * scene.ambientColor.a;
213
+ let sunDiffuse = scene.sunColor.rgb * NdotL;
214
+ let diffuseColor = surface.baseColor * (ambient + sunDiffuse) * diffuseWeight + surface.emission;
215
+ let specTerm = blinnPhongSpecular(surface.normal, L, V, surface.roughness);
216
+ let specular = scene.sunColor.rgb * specTerm * F * NdotL;
217
+ return diffuseColor + specular;`;
231
218
  }
232
219
 
233
220
  const EPSILON = 1e-7;
234
221
 
235
- export const RT_STRUCTS = /* wgsl */ `
222
+ const RT_STRUCTS = /* wgsl */ `
236
223
  ${RAY_STRUCT_WGSL}
237
224
  ${HIT_RESULT_STRUCT_WGSL}
238
225
  ${SURFACE_DATA_STRUCT_WGSL}
@@ -241,7 +228,7 @@ ${SKY_STRUCT_WGSL}
241
228
  ${DATA_STRUCT_WGSL}
242
229
  `;
243
230
 
244
- export const RT_BINDINGS = /* wgsl */ `
231
+ const RT_BINDINGS = /* wgsl */ `
245
232
  @group(0) @binding(0) var<uniform> scene: Scene;
246
233
  @group(0) @binding(1) var<storage, read> data: array<Data>;
247
234
  @group(0) @binding(2) var output_scene: texture_storage_2d<rgba8unorm, write>;
@@ -250,7 +237,7 @@ export const RT_BINDINGS = /* wgsl */ `
250
237
  @group(0) @binding(5) var<uniform> sky: Sky;
251
238
  `;
252
239
 
253
- export const RT_UTILS = /* wgsl */ `
240
+ const RT_UTILS = /* wgsl */ `
254
241
  fn getData(eid: u32) -> Data {
255
242
  return data[eid];
256
243
  }
@@ -272,7 +259,7 @@ fn getInstanceCount() -> u32 {
272
259
  }
273
260
  `;
274
261
 
275
- export const RT_RAY_GEN = /* wgsl */ `
262
+ const RT_RAY_GEN = /* wgsl */ `
276
263
  ${SKY_DIR_WGSL}
277
264
 
278
265
  struct PrimaryRay {
@@ -323,7 +310,7 @@ fn generateRay(screenX: f32, screenY: f32) -> PrimaryRay {
323
310
  }
324
311
  `;
325
312
 
326
- export const RT_INTERSECTION = /* wgsl */ `
313
+ const RT_INTERSECTION = /* wgsl */ `
327
314
  const EPSILON: f32 = ${EPSILON};
328
315
  `;
329
316
 
@@ -334,7 +321,7 @@ export function compileRTSurface(data: SurfaceData): string {
334
321
  function compileSurfaceVariant(id: number, data: SurfaceData): string {
335
322
  const vertexBody = compileVertexBody(data.vertex);
336
323
  const fragmentBody = data.fragment ?? "";
337
- const lightingCode = compileApplyLighting(data.lit, true, false);
324
+ const lightingCode = compileApplyLighting(true, false);
338
325
 
339
326
  return `
340
327
  fn userVertexTransform_${id}(worldPos: vec3<f32>, normal: vec3<f32>, eid: u32) -> vec3<f32> {
@@ -809,7 +796,3 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
809
796
  }
810
797
  `;
811
798
  }
812
-
813
- export function compileForwardShader(surface: SurfaceData): string {
814
- return compileUberShader([surface]);
815
- }
@@ -1,11 +1,7 @@
1
1
  import { setTraits } from "../../core/component";
2
2
  import { perspective, orthographic, multiply, invert, extractFrustumPlanes } from "../../core/math";
3
3
  import { WorldTransform } from "../transforms";
4
-
5
- export const RenderMode = {
6
- Raster: 0,
7
- Raytracing: 1,
8
- } as const;
4
+ import { SCENE_UNIFORM_SIZE, SKY_UNIFORM_SIZE } from "./scene";
9
5
 
10
6
  export const CameraMode = {
11
7
  Perspective: 0,
@@ -18,7 +14,6 @@ export const Camera = {
18
14
  far: [] as number[],
19
15
  active: [] as number[],
20
16
  clearColor: [] as number[],
21
- renderMode: [] as number[],
22
17
  mode: [] as number[],
23
18
  size: [] as number[],
24
19
  };
@@ -30,7 +25,6 @@ setTraits(Camera, {
30
25
  far: 1000,
31
26
  active: 1,
32
27
  clearColor: 0x1a1a1a,
33
- renderMode: RenderMode.Raster,
34
28
  mode: CameraMode.Perspective,
35
29
  size: 5,
36
30
  }),
@@ -46,8 +40,6 @@ setTraits(Tonemap, {
46
40
 
47
41
  export const FXAA = {};
48
42
 
49
- export const Raytracing = {};
50
-
51
43
  export const Vignette = {
52
44
  strength: [] as number[],
53
45
  inner: [] as number[],
@@ -80,17 +72,7 @@ setTraits(Quantize, {
80
72
  defaults: () => ({ bands: 8 }),
81
73
  });
82
74
 
83
- export const Shadows = {
84
- softness: [] as number[],
85
- samples: [] as number[],
86
- };
87
-
88
- setTraits(Shadows, {
89
- defaults: () => ({
90
- softness: 0.5,
91
- samples: 1,
92
- }),
93
- });
75
+ export const Shadows = {};
94
76
 
95
77
  export const Reflections = {
96
78
  depth: [] as number[],
@@ -199,7 +181,7 @@ setTraits(Viewport, {
199
181
  defaults: () => ({ width: 0, height: 0 }),
200
182
  });
201
183
 
202
- export interface SkyParams {
184
+ interface SkyParams {
203
185
  zenith: number;
204
186
  horizon: number;
205
187
  }
@@ -212,31 +194,31 @@ export function unpackColor(packed: number): { r: number; g: number; b: number }
212
194
  };
213
195
  }
214
196
 
215
- export interface HazeParams {
197
+ interface HazeParams {
216
198
  density: number;
217
199
  color: number;
218
200
  }
219
201
 
220
- export interface MoonParams {
202
+ interface MoonParams {
221
203
  phase: number;
222
204
  glow: number;
223
205
  azimuth: number;
224
206
  elevation: number;
225
207
  }
226
208
 
227
- export interface StarsParams {
209
+ interface StarsParams {
228
210
  intensity: number;
229
211
  amount: number;
230
212
  }
231
213
 
232
- export interface CloudsParams {
214
+ interface CloudsParams {
233
215
  coverage: number;
234
216
  density: number;
235
217
  height: number;
236
218
  color: number;
237
219
  }
238
220
 
239
- export interface SunParams {
221
+ interface SunParams {
240
222
  size: number;
241
223
  glow: number;
242
224
  color: number;
@@ -258,7 +240,7 @@ export function hasSkyComponent(
258
240
  return state.hasComponent(eid, Sky);
259
241
  }
260
242
 
261
- const sceneBuffer = new ArrayBuffer(352);
243
+ const sceneBuffer = new ArrayBuffer(SCENE_UNIFORM_SIZE);
262
244
  const scene = new Float32Array(sceneBuffer);
263
245
  const sceneU32 = new Uint32Array(sceneBuffer);
264
246
 
@@ -314,7 +296,7 @@ export function uploadCamera(
314
296
  device.queue.writeBuffer(buffer, 176, scene, 44, 44);
315
297
  }
316
298
 
317
- const skyArrayBuffer = new ArrayBuffer(176);
299
+ const skyArrayBuffer = new ArrayBuffer(SKY_UNIFORM_SIZE);
318
300
  const skyF32 = new Float32Array(skyArrayBuffer);
319
301
 
320
302
  export function uploadSky(
@@ -38,7 +38,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
38
38
  }
39
39
  `;
40
40
 
41
- export interface DataConfig {
41
+ interface DataConfig {
42
42
  colors: GPUBuffer;
43
43
  pbr: GPUBuffer;
44
44
  emission: GPUBuffer;
@@ -7,7 +7,6 @@ import {
7
7
  Camera,
8
8
  Tonemap,
9
9
  FXAA,
10
- Raytracing,
11
10
  Vignette,
12
11
  Bloom,
13
12
  Quantize,
@@ -25,6 +24,33 @@ import {
25
24
  uploadSky,
26
25
  } from "./camera";
27
26
  import { AmbientLight, DirectionalLight, packLightUniforms } from "./light";
27
+
28
+ export interface ShadowSettingsProvider {
29
+ softness: number[];
30
+ samples: number[];
31
+ isActive: (state: State, eid: number) => boolean;
32
+ }
33
+
34
+ const shadowSettingsProviders: ShadowSettingsProvider[] = [];
35
+
36
+ export function registerShadowSettings(settings: ShadowSettingsProvider): void {
37
+ shadowSettingsProviders.push(settings);
38
+ }
39
+
40
+ export function getShadowSettings(
41
+ state: State,
42
+ eid: number
43
+ ): { softness: number; samples: number } {
44
+ for (const provider of shadowSettingsProviders) {
45
+ if (provider.isActive(state, eid)) {
46
+ return {
47
+ softness: provider.softness[eid] ?? 0,
48
+ samples: Math.max(1, provider.samples[eid] ?? 16),
49
+ };
50
+ }
51
+ }
52
+ return { softness: 0, samples: 16 };
53
+ }
28
54
  import {
29
55
  Mesh,
30
56
  MeshShapes,
@@ -34,6 +60,7 @@ import {
34
60
  MeshEmission,
35
61
  MeshVolumes,
36
62
  createShapeAtlas,
63
+ getMeshVersion,
37
64
  } from "./mesh";
38
65
  import { Surface, SurfaceIds } from "./surface";
39
66
  import { createSceneBuffer, createSkyBuffer, ensureTextures } from "./scene";
@@ -45,10 +72,8 @@ import { createDataNode } from "./data";
45
72
  export {
46
73
  Camera,
47
74
  CameraMode,
48
- RenderMode,
49
75
  Tonemap,
50
76
  FXAA,
51
- Raytracing,
52
77
  Vignette,
53
78
  Bloom,
54
79
  Quantize,
@@ -67,14 +92,25 @@ export {
67
92
  uploadSky,
68
93
  } from "./camera";
69
94
  export { AmbientLight, DirectionalLight } from "./light";
70
- export { Mesh, MeshShape, Volume, mesh, getMesh, clearMeshes } from "./mesh";
71
- export { createBox } from "./mesh/box";
72
- export { createSphere } from "./mesh/sphere";
73
- export { createPlane } from "./mesh/plane";
74
- export type { MeshData } from "./mesh";
95
+ export {
96
+ Mesh,
97
+ MeshShape,
98
+ Volume,
99
+ mesh,
100
+ getMesh,
101
+ getMeshByName,
102
+ getMeshVersion,
103
+ meshCount,
104
+ clearMeshes,
105
+ computeShapeAABB,
106
+ createBox,
107
+ createSphere,
108
+ createPlane,
109
+ } from "./mesh";
110
+ export type { MeshData, AABB } from "./mesh";
75
111
  export { Surface, SurfaceType } from "./surface";
76
112
 
77
- export { surface, compose } from "./surface";
113
+ export { surface, getSurfaceByName, compose } from "./surface";
78
114
  export type { SurfaceData } from "./surface";
79
115
 
80
116
  export { Pass, registerDraw, unregisterDraw, getDrawsByPass, Draws } from "./pass";
@@ -99,6 +135,7 @@ export interface Render {
99
135
  width: number;
100
136
  height: number;
101
137
  entityCount: number;
138
+ meshVersion: number;
102
139
  scene: GPUBuffer;
103
140
  sky: GPUBuffer;
104
141
  matrices: GPUBuffer;
@@ -168,14 +205,33 @@ const RenderSystem: System = {
168
205
  resources.textureViews
169
206
  );
170
207
 
208
+ const currentMeshVersion = getMeshVersion();
209
+ if (currentMeshVersion !== render.meshVersion) {
210
+ render.meshVertices.destroy();
211
+ render.meshIndices.destroy();
212
+ render.meshMeta.destroy();
213
+ const shapeAtlas = createShapeAtlas(device);
214
+ render.meshVertices = shapeAtlas.vertices;
215
+ render.meshIndices = shapeAtlas.indices;
216
+ render.meshMeta = shapeAtlas.meta;
217
+ render.meshVersion = currentMeshVersion;
218
+ }
219
+
171
220
  render.entityCount = state.maxEid + 1;
172
221
  const uploadCount = render.entityCount;
173
222
 
174
223
  for (const eid of state.query([Camera])) {
175
224
  if (Camera.active[eid]) {
176
225
  const hasShadows = state.hasComponent(eid, Shadows);
177
- const shadowSoftness = hasShadows ? Shadows.softness[eid] : 0;
178
- const shadowSamples = hasShadows ? Math.max(1, Shadows.samples[eid]) : 0;
226
+
227
+ let shadowSoftness = 0;
228
+ let shadowSamples = 0;
229
+
230
+ if (hasShadows) {
231
+ const shadowSettings = getShadowSettings(state, eid);
232
+ shadowSoftness = shadowSettings.softness;
233
+ shadowSamples = shadowSettings.samples;
234
+ }
179
235
 
180
236
  const hasReflections = state.hasComponent(eid, Reflections);
181
237
  const reflectionDepth = hasReflections
@@ -368,7 +424,6 @@ export const RenderPlugin: Plugin = {
368
424
  DirectionalLight,
369
425
  Tonemap,
370
426
  FXAA,
371
- Raytracing,
372
427
  Vignette,
373
428
  Bloom,
374
429
  Quantize,
@@ -412,6 +467,7 @@ export const RenderPlugin: Plugin = {
412
467
  surfaces: createPropertyBuffer(MAX_ENTITIES * 4, "surfaces"),
413
468
  data: createPropertyBuffer(MAX_ENTITIES * 64, "data"),
414
469
  entityCount: 1,
470
+ meshVersion: getMeshVersion(),
415
471
  postProcess: {
416
472
  tonemap: false,
417
473
  exposure: 1.0,
@@ -36,12 +36,12 @@ export function normalizeDirection(x: number, y: number, z: number): [number, nu
36
36
  return [x / len, y / len, z / len];
37
37
  }
38
38
 
39
- export interface AmbientLightData {
39
+ interface AmbientLightData {
40
40
  color: number;
41
41
  intensity: number;
42
42
  }
43
43
 
44
- export interface DirectionalLightData {
44
+ interface DirectionalLightData {
45
45
  color: number;
46
46
  intensity: number;
47
47
  directionX: number;