reze-engine 0.15.0 → 0.16.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 (93) hide show
  1. package/README.md +3 -3
  2. package/dist/camera.d.ts +2 -0
  3. package/dist/camera.d.ts.map +1 -1
  4. package/dist/camera.js +8 -3
  5. package/dist/engine.d.ts +19 -4
  6. package/dist/engine.d.ts.map +1 -1
  7. package/dist/engine.js +294 -27
  8. package/dist/gpu-profile.d.ts +19 -0
  9. package/dist/gpu-profile.d.ts.map +1 -0
  10. package/dist/gpu-profile.js +120 -0
  11. package/dist/ik-solver.d.ts.map +1 -1
  12. package/dist/ik-solver.js +13 -7
  13. package/dist/math.d.ts +2 -0
  14. package/dist/math.d.ts.map +1 -1
  15. package/dist/math.js +65 -46
  16. package/dist/model.d.ts +28 -1
  17. package/dist/model.d.ts.map +1 -1
  18. package/dist/model.js +259 -75
  19. package/dist/physics/body.d.ts +2 -0
  20. package/dist/physics/body.d.ts.map +1 -1
  21. package/dist/physics/body.js +30 -0
  22. package/dist/physics/constraint.d.ts +13 -0
  23. package/dist/physics/constraint.d.ts.map +1 -1
  24. package/dist/physics/constraint.js +13 -0
  25. package/dist/physics/contact.d.ts +28 -0
  26. package/dist/physics/contact.d.ts.map +1 -1
  27. package/dist/physics/contact.js +27 -32
  28. package/dist/physics/physics.d.ts +3 -0
  29. package/dist/physics/physics.d.ts.map +1 -1
  30. package/dist/physics/physics.js +57 -8
  31. package/dist/physics/profile.d.ts +18 -0
  32. package/dist/physics/profile.d.ts.map +1 -0
  33. package/dist/physics/profile.js +44 -0
  34. package/dist/physics/solver.d.ts.map +1 -1
  35. package/dist/physics/solver.js +436 -293
  36. package/dist/pmx-loader.d.ts.map +1 -1
  37. package/dist/pmx-loader.js +2 -30
  38. package/dist/shaders/materials/body.d.ts +1 -1
  39. package/dist/shaders/materials/body.d.ts.map +1 -1
  40. package/dist/shaders/materials/body.js +3 -1
  41. package/dist/shaders/materials/cloth_rough.d.ts +1 -1
  42. package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
  43. package/dist/shaders/materials/cloth_rough.js +3 -1
  44. package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
  45. package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
  46. package/dist/shaders/materials/common.d.ts +2 -2
  47. package/dist/shaders/materials/common.d.ts.map +1 -1
  48. package/dist/shaders/materials/common.js +8 -0
  49. package/dist/shaders/materials/default.d.ts +1 -1
  50. package/dist/shaders/materials/default.d.ts.map +1 -1
  51. package/dist/shaders/materials/eye.d.ts +1 -1
  52. package/dist/shaders/materials/eye.d.ts.map +1 -1
  53. package/dist/shaders/materials/face.d.ts +1 -1
  54. package/dist/shaders/materials/face.d.ts.map +1 -1
  55. package/dist/shaders/materials/face.js +4 -1
  56. package/dist/shaders/materials/hair.d.ts +1 -1
  57. package/dist/shaders/materials/hair.d.ts.map +1 -1
  58. package/dist/shaders/materials/metal.d.ts +1 -1
  59. package/dist/shaders/materials/metal.d.ts.map +1 -1
  60. package/dist/shaders/materials/stockings.d.ts +1 -1
  61. package/dist/shaders/materials/stockings.d.ts.map +1 -1
  62. package/dist/shaders/materials/stockings.js +13 -9
  63. package/dist/shaders/passes/composite.d.ts +1 -1
  64. package/dist/shaders/passes/composite.d.ts.map +1 -1
  65. package/dist/shaders/passes/composite.js +18 -12
  66. package/dist/shaders/passes/morph.d.ts +2 -0
  67. package/dist/shaders/passes/morph.d.ts.map +1 -0
  68. package/dist/shaders/passes/morph.js +49 -0
  69. package/package.json +1 -1
  70. package/src/camera.ts +9 -3
  71. package/src/engine.ts +336 -29
  72. package/src/ik-solver.ts +14 -7
  73. package/src/math.ts +41 -51
  74. package/src/model.ts +288 -91
  75. package/src/physics/body.ts +29 -0
  76. package/src/physics/constraint.ts +30 -0
  77. package/src/physics/contact.ts +43 -30
  78. package/src/physics/physics.ts +58 -17
  79. package/src/physics/solver.ts +431 -284
  80. package/src/pmx-loader.ts +2 -35
  81. package/src/shaders/materials/body.ts +3 -1
  82. package/src/shaders/materials/cloth_rough.ts +3 -1
  83. package/src/shaders/materials/common.ts +8 -0
  84. package/src/shaders/materials/face.ts +4 -1
  85. package/src/shaders/materials/stockings.ts +13 -9
  86. package/src/shaders/passes/composite.ts +18 -12
  87. package/src/shaders/passes/morph.ts +50 -0
  88. package/dist/physics-debug.d.ts +0 -30
  89. package/dist/physics-debug.d.ts.map +0 -1
  90. package/dist/physics-debug.js +0 -526
  91. package/dist/shaders/passes/physics-debug.d.ts +0 -2
  92. package/dist/shaders/passes/physics-debug.d.ts.map +0 -1
  93. package/dist/shaders/passes/physics-debug.js +0 -69
package/src/pmx-loader.ts CHANGED
@@ -1017,43 +1017,10 @@ export class PmxLoader {
1017
1017
  skinning = { joints, weights }
1018
1018
  }
1019
1019
 
1020
- // Create morphing data structure
1021
- const morphCount = this.morphs.length
1022
- // Dense buffer: morphCount * vertexCount * 3 floats (one vec3 per morph per vertex)
1023
- const offsetsBuffer = new Float32Array(morphCount * this.vertexCount * 3)
1024
-
1025
- // Initialize all offsets to zero
1026
- offsetsBuffer.fill(0)
1027
-
1028
- // Fill in actual offsets for vertex morphs
1029
- for (let morphIdx = 0; morphIdx < morphCount; morphIdx++) {
1030
- const morph = this.morphs[morphIdx]
1031
- if (morph.type === 1) {
1032
- // Vertex morph
1033
- // Store offsets in dense buffer: [morph0_v0, morph0_v1, ..., morph1_v0, ...]
1034
- // Each vec3 is 3 consecutive floats
1035
- for (const offset of morph.vertexOffsets) {
1036
- // Calculate index in the dense buffer
1037
- // Layout: morphIdx * (vertexCount * 3) + vertexIndex * 3
1038
- // This gives us the starting float index for this morph's vertex offset
1039
- const bufferIdx = morphIdx * this.vertexCount * 3 + offset.vertexIndex * 3
1040
- if (
1041
- bufferIdx >= 0 &&
1042
- bufferIdx + 2 < offsetsBuffer.length &&
1043
- offset.vertexIndex >= 0 &&
1044
- offset.vertexIndex < this.vertexCount
1045
- ) {
1046
- offsetsBuffer[bufferIdx] = offset.positionOffset[0]
1047
- offsetsBuffer[bufferIdx + 1] = offset.positionOffset[1]
1048
- offsetsBuffer[bufferIdx + 2] = offset.positionOffset[2]
1049
- }
1050
- }
1051
- }
1052
- }
1053
-
1020
+ // Morphs are applied from the sparse per-morph vertexOffsets lists (see
1021
+ // Model.applyMorphs); no dense morphCount*vertexCount buffer is needed.
1054
1022
  const morphing: Morphing = {
1055
1023
  morphs: this.morphs,
1056
- offsetsBuffer,
1057
1024
  }
1058
1025
 
1059
1026
  return new Model(
@@ -67,7 +67,9 @@ fn ramp_ease(f: f32, p0: f32, c0: vec4f, p1: f32, c1: vec4f) -> vec4f {
67
67
 
68
68
  // ═══ PRINCIPLED BSDF with noise bump ═══
69
69
  // Mapping loc=rot=0 in the Blender graph folds to a plain scale multiply.
70
- let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
70
+ // Height in rest space (restPos) so the bump rides with the skin; geometric basis stays
71
+ // worldPos so the perturbed normal matches the world-space n (see face.ts note).
72
+ let noise_val = tex_noise_d2(input.restPos * vec3f(1.0, 1.0, 1.5), 1.0);
71
73
  let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
72
74
  let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
73
75
 
@@ -48,7 +48,9 @@ const CLOTH_R_SPEC_CLAMP: f32 = 10.0;
48
48
  let npr_emission = npr_rgb * CLOTH_R_EMIT_STR;
49
49
 
50
50
  // ═══ PRINCIPLED BSDF with noise bump (live in this preset) ═══
51
- let noise_val = tex_noise_d2(input.worldPos, CLOTH_R_NOISE_SCALE);
51
+ // Height in rest space (restPos) so the weave doesn't swim; geometric basis stays
52
+ // worldPos so the perturbed normal matches the world-space n (see face.ts note).
53
+ let noise_val = tex_noise_d2(input.restPos, CLOTH_R_NOISE_SCALE);
52
54
  let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
53
55
  let bumped_n = bump_lh(CLOTH_R_BUMP_STR, noise_ramp, n, input.worldPos);
54
56
 
@@ -51,6 +51,13 @@ struct VertexOutput {
51
51
  @location(0) normal: vec3f,
52
52
  @location(1) uv: vec2f,
53
53
  @location(2) worldPos: vec3f,
54
+ // Bind-pose object-space position (the raw pre-skin vertex attribute). Procedural
55
+ // textures (noise bump, sparkle, Generated-coord gradients) key off this instead of
56
+ // worldPos so the pattern rides with the surface — otherwise the mesh swims through a
57
+ // world-static noise field under any skinning deformation or root (センター) motion.
58
+ // At rest skinMats are identity so restPos == worldPos, which is why existing noise-
59
+ // scale constants stay valid without retuning.
60
+ @location(3) restPos: vec3f,
54
61
  };
55
62
 
56
63
  struct LightVP { viewProj: mat4x4f, };
@@ -129,6 +136,7 @@ export const COMMON_VS_WGSL = /* wgsl */ `
129
136
  output.normal = skinnedNrm;
130
137
  output.uv = uv;
131
138
  output.worldPos = skinnedPos.xyz;
139
+ output.restPos = position;
132
140
  return output;
133
141
  }
134
142
 
@@ -66,7 +66,10 @@ const FACE_BRIGHT_TEX_THRESH: f32 = 0.9300000071525574;
66
66
  let npr_stack = rim1 + rim2_mixed + bright_emit + warm_emission;
67
67
 
68
68
  // ═══ PRINCIPLED BSDF with noise bump ═══
69
- let noise_val = tex_noise_d2(input.worldPos * vec3f(1.0, 1.0, 1.5), 1.0);
69
+ // Height sampled in rest space (restPos) so the micro-bump doesn't swim under motion;
70
+ // bump_lh's geometric basis stays in worldPos so the perturbed normal lands in the same
71
+ // world frame as n (screen-space height gradient still pairs correctly with it).
72
+ let noise_val = tex_noise_d2(input.restPos * vec3f(1.0, 1.0, 1.5), 1.0);
70
73
  let noise_ramp = ramp_linear(noise_val, 0.0, vec4f(0,0,0,1), 1.0, vec4f(1,1,1,1)).r;
71
74
  let bumped_n = bump_lh(0.324644535779953, noise_ramp, n, input.worldPos);
72
75
 
@@ -22,15 +22,19 @@ const STOCK_RAMP002_P1: f32 = 0.9565; // EASE [0→black, 0.9565→white]
22
22
  const STOCK_RAMPFACE_P1: f32 = 0.5435; // EASE [0→black, 0.5435→white]
23
23
  const STOCK_LW_BLEND: f32 = 0.4; // Layer Weight Blend
24
24
 
25
- // Wyman & McGuire "Hashed Alpha Testing" (2017) — world-space hash with derivative-aware
25
+ // Wyman & McGuire "Hashed Alpha Testing" (2017) — object-space hash with derivative-aware
26
26
  // pixel-scale selection, matches Blender EEVEE prepass_frag.glsl::hashed_alpha_threshold.
27
- // Key property: dither pattern is stable in object/world space (doesn't swim) and stays
28
- // at one-pixel frequency regardless of view distance, which makes it tolerable without TAA.
29
- fn _hash_wm(a: vec2f) -> f32 {
30
- return fract(1e4 * sin(17.0 * a.x + 0.1 * a.y) * (0.1 + abs(sin(13.0 * a.y + a.x))));
31
- }
27
+ // Key property: dither pattern is stable in object space (doesn't swim) and stays at
28
+ // one-pixel frequency regardless of view distance, which makes it tolerable without TAA.
29
+ // The hash grid is fed the bind-pose restPos (not worldPos), so the dots stay pinned to
30
+ // the fabric even under root (センター) motion worldPos would crawl across the surface.
31
+ // Since MMD skinning is rigid, length(dpdx(restPos)) == length(dpdx(worldPos)), so the
32
+ // pixel-scale selection is unaffected by the space change.
33
+ // Grid samples reuse the PCG _hash33 from nodes.ts (integer-lattice inputs, Metal-friendly)
34
+ // instead of a fract(sin()) hash — sin compiles to a slow transcendental on Apple's Metal
35
+ // backend and this runs per-fragment on every stockings pixel.
32
36
  fn _hash3d_wm(a: vec3f) -> f32 {
33
- return _hash_wm(vec2f(_hash_wm(a.xy), a.z));
37
+ return _hash33(a).x * 0.5 + 0.5;
34
38
  }
35
39
  fn hashed_alpha_threshold(co: vec3f) -> f32 {
36
40
  let alphaHashScale: f32 = 1.0;
@@ -72,12 +76,12 @@ fn ramp_ease_s(f: f32, p0: f32, p1: f32) -> f32 {
72
76
  let tex_s = textureSample(diffuseTexture, diffuseSampler, input.uv);
73
77
  let tex_rgb = tex_s.rgb;
74
78
  // Alpha HASHED (Blender EEVEE "Hashed" blend mode) per preset author's note — self-overlap
75
- // on the stockings produces sort cracks under alpha blend. Wyman-style worldPos hash +
79
+ // on the stockings produces sort cracks under alpha blend. Wyman-style object-space hash +
76
80
  // depth-write is sort-independent. NOTE: Principled.Alpha=0.95 from the dump is DROPPED;
77
81
  // it relies on TAA to smooth the 5%-everywhere dither, and without TAA it shows as a
78
82
  // pervasive dot pattern. Hash now gates only on texture/material alpha.
79
83
  let combined_alpha = material.alpha * tex_s.a;
80
- if (combined_alpha < hashed_alpha_threshold(input.worldPos)) { discard; }
84
+ if (combined_alpha < hashed_alpha_threshold(input.restPos)) { discard; }
81
85
 
82
86
  // ═══ NPR MASK ═══ TEX_COORD.Generated → Mapping(Rot=0,π/2,π/2, Loc=(1,1,1)) → Gradient.
83
87
  // The Blender mapping reduces to gradient.x = 1 - input.y (rot swaps axes, loc offsets).
@@ -19,23 +19,29 @@ override APPLY_GAMMA: bool = true;
19
19
  // re-premultiply the tonemapped color for output so the premultiplied canvas
20
20
  // alphaMode composites the WebGPU surface over the page background correctly.
21
21
  @group(0) @binding(4) var maskTex: texture_2d<f32>;
22
+ // Filmic tone curve baked to a WIDTH×1 r16float LUT (bakeFilmicLut on the CPU side).
23
+ // Domain: log2(linear) mapped to [0,13]. Replaces the old array<f32,14> that was indexed
24
+ // by a runtime u32 (a Metal-backend smell — dynamic local-array indexing lowers to a
25
+ // per-invocation copy/switch) and interpolated piecewise-linearly (C0, so segment slope
26
+ // discontinuities showed as Mach bands in smooth skin/shadow gradients). The LUT is a
27
+ // monotone-cubic (Fritsch–Carlson) fit through the same 14 anchors — same values, C1
28
+ // continuity kills the banding — sampled with hardware linear filtering.
29
+ @group(0) @binding(5) var filmicLut: texture_2d<f32>;
22
30
  // viewU[0] = (exposure, invGamma, _, _); viewU[1] = (tint.rgb, intensity)
23
31
  // invGamma = 1/gamma precomputed on CPU — avoids a per-pixel divide.
24
32
 
33
+ // Must match FILMIC_LUT_WIDTH in engine.ts (bakeFilmicLut).
34
+ const FILMIC_LUT_W: f32 = 256.0;
35
+
25
36
  fn filmic(x: f32) -> f32 {
26
- // Re-fit against Blender 3.6 Filmic MHC anchors (sobotka/filmic-blender
27
- // look_medium-high-contrast.spi1d). Previous curve was compressed:
28
- // midtones too bright, highlights too dim flattened contrast, read
29
- // as "washed-out" on saturated surfaces (hair especially).
30
- // Reference checkpoints: linear 0.18 → ~0.395, linear 1.0 → ~0.83.
31
- var lut = array<f32, 14>(
32
- 0.0028, 0.0068, 0.0151, 0.0313, 0.0610, 0.1120, 0.1920,
33
- 0.3060, 0.4590, 0.6310, 0.8200, 0.9070, 0.9620, 0.9890
34
- );
37
+ // Reference checkpoints (Blender 3.6 Filmic MHC, sobotka/filmic-blender
38
+ // look_medium-high-contrast.spi1d): linear 0.18 ~0.395, linear 1.0 → ~0.83.
39
+ // NOTE: version-pinned to Blender 3.64.x defaults to AgX, not Filmic.
35
40
  let t = clamp(log2(max(x, 1e-10)) + 10.0, 0.0, 13.0);
36
- let i = u32(t);
37
- let j = min(i + 1u, 13u);
38
- return mix(lut[i], lut[j], t - f32(i));
41
+ // Map t∈[0,13] to the texel-center of baked sample j = (W-1)/13.
42
+ let u = (t * (FILMIC_LUT_W - 1.0) / 13.0 + 0.5) / FILMIC_LUT_W;
43
+ // textureSampleLevel (explicit LOD, no derivatives) is legal in non-uniform flow.
44
+ return textureSampleLevel(filmicLut, bloomSamp, vec2f(u, 0.5), 0.0).r;
39
45
  }
40
46
 
41
47
  @vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
@@ -0,0 +1,50 @@
1
+ // GPU vertex-morph compute pass. Produces per-vertex morphed positions and writes them
2
+ // straight into the interleaved vertex buffer (stride 8: pos3 + normal3 + uv2), so every
3
+ // downstream pass (main / shadow / outline / pick) sees the morphed geometry with no VS
4
+ // change. Runs once per frame only when a model's morph weights changed.
5
+ //
6
+ // Morph data is a CSR (compressed sparse row) inversion of the per-morph vertex-offset
7
+ // lists: for vertex v, its entries live in [rowStart[v], rowStart[v+1]); each entry is a
8
+ // (morphIndex, xyz-offset) pair, ordered by ascending morph index — the same accumulation
9
+ // order the CPU path used, so results match. Group morphs are resolved to effective
10
+ // weights on the CPU and uploaded in `weights`.
11
+
12
+ export const MORPH_COMPUTE_WGSL = /* wgsl */ `
13
+ struct Params { vertexCount: u32 };
14
+
15
+ @group(0) @binding(0) var<storage, read> basePos: array<f32>; // vertexCount * 3
16
+ @group(0) @binding(1) var<storage, read> rowStart: array<u32>; // vertexCount + 1
17
+ @group(0) @binding(2) var<storage, read> colMorph: array<u32>; // entryCount
18
+ @group(0) @binding(3) var<storage, read> colOffset: array<f32>; // entryCount * 3
19
+ @group(0) @binding(4) var<storage, read> weights: array<f32>; // morphCount (effective)
20
+ @group(0) @binding(5) var<storage, read_write> vtx: array<f32>; // vertexCount * 8
21
+ @group(0) @binding(6) var<uniform> params: Params;
22
+
23
+ @compute @workgroup_size(64)
24
+ fn cs(@builtin(global_invocation_id) gid: vec3<u32>) {
25
+ let v = gid.x;
26
+ if (v >= params.vertexCount) { return; }
27
+
28
+ let b3 = v * 3u;
29
+ var px = basePos[b3];
30
+ var py = basePos[b3 + 1u];
31
+ var pz = basePos[b3 + 2u];
32
+
33
+ let s = rowStart[v];
34
+ let e = rowStart[v + 1u];
35
+ for (var i = s; i < e; i = i + 1u) {
36
+ let w = weights[colMorph[i]];
37
+ if (w != 0.0) {
38
+ let o3 = i * 3u;
39
+ px = px + w * colOffset[o3];
40
+ py = py + w * colOffset[o3 + 1u];
41
+ pz = pz + w * colOffset[o3 + 2u];
42
+ }
43
+ }
44
+
45
+ let vo = v * 8u; // VERTEX_STRIDE — write only the position slots
46
+ vtx[vo] = px;
47
+ vtx[vo + 1u] = py;
48
+ vtx[vo + 2u] = pz;
49
+ }
50
+ `
@@ -1,30 +0,0 @@
1
- import type { RezePhysics } from "./physics";
2
- export declare class PhysicsDebugRenderer {
3
- private device;
4
- private bindGroup;
5
- private wirePipelineSphere;
6
- private wirePipelineBox;
7
- private wirePipelineCapsule;
8
- private wireSphereBuffer;
9
- private wireBoxBuffer;
10
- private wireCapsuleBuffer;
11
- private wireSphereCount;
12
- private wireBoxCount;
13
- private wireCapsuleCount;
14
- private solidPipelineSphere;
15
- private solidPipelineBox;
16
- private solidPipelineCapsule;
17
- private solidSphereBuffer;
18
- private solidBoxBuffer;
19
- private solidCapsuleBuffer;
20
- private solidSphereCount;
21
- private solidBoxCount;
22
- private solidCapsuleCount;
23
- private instanceBuffer;
24
- private instanceData;
25
- private instanceCapacity;
26
- constructor(device: GPUDevice, cameraUniformBuffer: GPUBuffer, presentationFormat: GPUTextureFormat);
27
- render(pass: GPURenderPassEncoder, physics: RezePhysics): void;
28
- destroy(): void;
29
- }
30
- //# sourceMappingURL=physics-debug.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"physics-debug.d.ts","sourceRoot":"","sources":["../src/physics-debug.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAmB5C,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,SAAS,CAAc;IAG/B,OAAO,CAAC,kBAAkB,CAAmB;IAC7C,OAAO,CAAC,eAAe,CAAmB;IAC1C,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,OAAO,CAAC,gBAAgB,CAAW;IACnC,OAAO,CAAC,aAAa,CAAW;IAChC,OAAO,CAAC,iBAAiB,CAAW;IACpC,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,gBAAgB,CAAQ;IAIhC,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,oBAAoB,CAAmB;IAC/C,OAAO,CAAC,iBAAiB,CAAW;IACpC,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,kBAAkB,CAAW;IACrC,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,iBAAiB,CAAQ;IAEjC,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,gBAAgB,CAAQ;gBAG9B,MAAM,EAAE,SAAS,EACjB,mBAAmB,EAAE,SAAS,EAC9B,kBAAkB,EAAE,gBAAgB;IA2HtC,MAAM,CAAC,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAmI9D,OAAO,IAAI,IAAI;CAShB"}