reze-engine 0.66.5 → 0.67.1

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 (73) hide show
  1. package/dist/camera.d.ts +1 -0
  2. package/dist/camera.d.ts.map +1 -1
  3. package/dist/camera.js +9 -5
  4. package/dist/engine.d.ts +20 -1
  5. package/dist/engine.d.ts.map +1 -1
  6. package/dist/engine.js +91 -28
  7. package/dist/graph/presets/body.js +1 -1
  8. package/dist/graph/presets/cloth_rough.js +1 -1
  9. package/dist/graph/presets/cloth_smooth.js +1 -1
  10. package/dist/graph/presets/face.js +1 -1
  11. package/dist/graph/presets/metal.js +1 -1
  12. package/dist/graph/presets/pool_floor.d.ts +3 -0
  13. package/dist/graph/presets/pool_floor.d.ts.map +1 -0
  14. package/dist/graph/presets/pool_floor.js +42 -0
  15. package/dist/graph/presets/water.d.ts +3 -0
  16. package/dist/graph/presets/water.d.ts.map +1 -0
  17. package/dist/graph/presets/water.js +31 -0
  18. package/dist/graph/registry.d.ts.map +1 -1
  19. package/dist/graph/registry.js +84 -6
  20. package/dist/graph/schema.d.ts +14 -0
  21. package/dist/graph/schema.d.ts.map +1 -1
  22. package/dist/physics/autofit.d.ts +147 -0
  23. package/dist/physics/autofit.d.ts.map +1 -0
  24. package/dist/physics/autofit.js +501 -0
  25. package/dist/shaders/agx-lut.d.ts +11 -0
  26. package/dist/shaders/agx-lut.d.ts.map +1 -0
  27. package/dist/shaders/agx-lut.js +29 -0
  28. package/dist/shaders/dfg_lut.d.ts +3 -0
  29. package/dist/shaders/dfg_lut.d.ts.map +1 -0
  30. package/dist/shaders/dfg_lut.js +129 -0
  31. package/dist/shaders/ltc_mag_lut.d.ts +3 -0
  32. package/dist/shaders/ltc_mag_lut.d.ts.map +1 -0
  33. package/dist/shaders/ltc_mag_lut.js +1033 -0
  34. package/dist/shaders/materials/nodes.d.ts +1 -1
  35. package/dist/shaders/materials/nodes.d.ts.map +1 -1
  36. package/dist/shaders/materials/nodes.js +176 -7
  37. package/dist/shaders/passes/composite.d.ts.map +1 -1
  38. package/dist/shaders/passes/composite.js +4 -2
  39. package/dist/shaders/passes/field-blit.d.ts +26 -0
  40. package/dist/shaders/passes/field-blit.d.ts.map +1 -0
  41. package/dist/shaders/passes/field-blit.js +65 -0
  42. package/dist/shaders/passes/ghosts.d.ts +22 -0
  43. package/dist/shaders/passes/ghosts.d.ts.map +1 -0
  44. package/dist/shaders/passes/ghosts.js +75 -0
  45. package/dist/shaders/passes/ground-noise.d.ts +7 -0
  46. package/dist/shaders/passes/ground-noise.d.ts.map +1 -0
  47. package/dist/shaders/passes/ground-noise.js +88 -0
  48. package/dist/shaders/passes/outline.d.ts +7 -0
  49. package/dist/shaders/passes/outline.d.ts.map +1 -1
  50. package/dist/shaders/passes/outline.js +67 -16
  51. package/dist/shaders/passes/sim.d.ts +34 -0
  52. package/dist/shaders/passes/sim.d.ts.map +1 -0
  53. package/dist/shaders/passes/sim.js +169 -0
  54. package/dist/shaders/score-api.d.ts +10 -0
  55. package/dist/shaders/score-api.d.ts.map +1 -0
  56. package/dist/shaders/score-api.js +114 -0
  57. package/dist/shaders/shadow-api.d.ts +29 -0
  58. package/dist/shaders/shadow-api.d.ts.map +1 -0
  59. package/dist/shaders/shadow-api.js +112 -0
  60. package/package.json +1 -1
  61. package/src/camera.ts +9 -5
  62. package/src/engine.ts +109 -30
  63. package/src/graph/presets/body.ts +1 -1
  64. package/src/graph/presets/cloth_rough.ts +1 -1
  65. package/src/graph/presets/cloth_smooth.ts +1 -1
  66. package/src/graph/presets/face.ts +1 -1
  67. package/src/graph/presets/metal.ts +1 -1
  68. package/src/graph/registry.ts +85 -6
  69. package/src/graph/schema.ts +14 -0
  70. package/src/shaders/materials/nodes.ts +176 -7
  71. package/src/shaders/passes/composite.ts +4 -2
  72. package/src/shaders/passes/outline.ts +67 -16
  73. package/dist/_dbg-loader.mjs +0 -1100
package/dist/engine.js CHANGED
@@ -36,7 +36,7 @@ import { sceneTargets as sceneTargetsFor, sceneColorFormats, setMrtIds, mrtIdsEn
36
36
  import { LIGHT_GRID_BASE, LIGHT_HEADER, LIGHT_STRIDE, LIGHTS_FLOATS, MAX_LIGHTS, buildLightEmitShader, hasLightEmit, } from "./shaders/lights";
37
37
  import { buildLightGrid } from "./light-grid";
38
38
  import { groundShaderWgsl, GROUND_NOISE_BAKE_WGSL, GROUND_NOISE_SIZE } from "./shaders/passes/ground";
39
- import { outlineShaderWgsl, RZ_OUTLINE_COLOR_OFFSET, RZ_OUTLINE_DISSOLVE_OFFSET, RZ_OUTLINE_WIDTH_OFFSET } from "./shaders/passes/outline";
39
+ import { outlineShaderWgsl, RZ_OUTLINE_COLOR_OFFSET, RZ_OUTLINE_DISSOLVE_OFFSET, RZ_OUTLINE_SHADOW_OFFSET, RZ_OUTLINE_TEXTURED_OFFSET, RZ_OUTLINE_WIDTH_OFFSET, } from "./shaders/passes/outline";
40
40
  import { transparentDepthPrepassWgsl } from "./shaders/passes/depth-prepass";
41
41
  import { SELECTION_MASK_SHADER_WGSL, SELECTION_EDGE_SHADER_WGSL } from "./shaders/passes/selection";
42
42
  import { GIZMO_SHADER_WGSL } from "./shaders/passes/gizmo";
@@ -101,7 +101,7 @@ const PRESET_NAME_HINTS = [
101
101
  "hair", "ahoge", "bang",
102
102
  ],
103
103
  ],
104
- ["body", ["肌", "皮肤", "skin"]],
104
+ ["body", ["肌", "皮肤", "skin", "指甲", "nail"]],
105
105
  ["metal", ["金属", "メタル", "metal", "earring", "耳环", "耳環"]],
106
106
  [
107
107
  "cloth_smooth",
@@ -146,6 +146,10 @@ const PRESET_NAME_HINTS = [
146
146
  ],
147
147
  ],
148
148
  ];
149
+ // Whole names that are body parts — skin by another name. Whole, never
150
+ // substrings: 手 alone is a hand, inside 手套 (gloves) or 手镯 (a bracelet) it is
151
+ // what the hand wears; 足 is a leg, 足袋 a sock.
152
+ const BODY_PART_NAMES = new Set(["手", "手部", "腕", "足", "脚", "腿", "爪", "hand", "hands", "arm", "arms", "leg", "legs", "body"]);
149
153
  // Resolve a material name to a style category (override map first, then name hints), or
150
154
  // null if nothing matches — a null-resolving material stays ungrouped (neutral default).
151
155
  function resolvePreset(materialName, map) {
@@ -156,6 +160,8 @@ function resolvePreset(materialName, map) {
156
160
  }
157
161
  }
158
162
  const lower = materialName.toLowerCase();
163
+ if (BODY_PART_NAMES.has(lower.replace(/[\s_.\-0-9]+$/, "")))
164
+ return "body";
159
165
  for (const [preset, hints] of PRESET_NAME_HINTS) {
160
166
  for (const hint of hints) {
161
167
  if (lower.includes(hint))
@@ -245,6 +251,7 @@ export const DEFAULT_DEPTH_OF_FIELD_OPTIONS = {
245
251
  export const DEFAULT_VIEW_TRANSFORM = {
246
252
  exposure: 0,
247
253
  gamma: 1.0,
254
+ contrast: 1.4,
248
255
  transform: "soft",
249
256
  };
250
257
  const NEUTRAL_GRADE_CHANNEL = 0.5;
@@ -1071,7 +1078,7 @@ export class Engine {
1071
1078
  // 15 × vec4f — see the viewU comment in composite.ts. The last one is the
1072
1079
  // camera's world position, which is what lets a foreground effect turn the
1073
1080
  // depth it is handed into a PLACE (bgWorldPos) rather than a distance.
1074
- this.compositeUniformData = new Float32Array(60);
1081
+ this.compositeUniformData = new Float32Array(64);
1075
1082
  /** Composite background (display-space sRGB 0–1) — null = transparent canvas. */
1076
1083
  this.backgroundColor = null;
1077
1084
  // 360 backdrop (equirectangular skybox, sampled by view ray in composite).
@@ -1765,6 +1772,7 @@ export class Engine {
1765
1772
  return {
1766
1773
  exposure: partial?.exposure ?? d.exposure,
1767
1774
  gamma: partial?.gamma ?? d.gamma,
1775
+ contrast: partial?.contrast ?? d.contrast,
1768
1776
  transform: partial?.transform ?? d.transform,
1769
1777
  };
1770
1778
  }
@@ -1781,7 +1789,7 @@ export class Engine {
1781
1789
  }
1782
1790
  getViewTransformOptions() {
1783
1791
  const v = this.viewTransform;
1784
- return { exposure: v.exposure, gamma: v.gamma, transform: v.transform };
1792
+ return { exposure: v.exposure, gamma: v.gamma, contrast: v.contrast, transform: v.transform };
1785
1793
  }
1786
1794
  /**
1787
1795
  * Color-grade the tonemapped scene (ASC CDL slope/offset/power + saturation).
@@ -1891,6 +1899,8 @@ export class Engine {
1891
1899
  v.exposure = patch.exposure;
1892
1900
  if (patch.gamma !== undefined)
1893
1901
  v.gamma = patch.gamma;
1902
+ if (patch.contrast !== undefined)
1903
+ v.contrast = patch.contrast;
1894
1904
  if (patch.transform !== undefined)
1895
1905
  v.transform = patch.transform;
1896
1906
  if (this.device && this.compositeUniformBuffer) {
@@ -1960,6 +1970,9 @@ export class Engine {
1960
1970
  u[25] = Engine.VIEW_TRANSFORM_ID[v.transform] ?? 0;
1961
1971
  u[26] = this.canvas.width;
1962
1972
  u[27] = this.canvas.height;
1973
+ // The soft curve's power (viewU[15].x), the scene's own (SceneSetting._contrast).
1974
+ // Its own vec4: viewU[11..14] are the cast's positions whenever an effect runs.
1975
+ u[60] = v.contrast;
1963
1976
  // ── Grade (viewU[7..9]) ── The UI's three tonal COLORS map to ASC CDL here,
1964
1977
  // on the CPU, so the shader only ever sees slope/offset/power. Mid-gray is
1965
1978
  // neutral in all three; the signed distance from it is the amount.
@@ -6832,6 +6845,8 @@ export class Engine {
6832
6845
  entries: [
6833
6846
  { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
6834
6847
  { binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: "filtering" } },
6848
+ // the key light, for a style graph's outline colour (see outline.ts)
6849
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
6835
6850
  ],
6836
6851
  });
6837
6852
  // Outline per-instance reuses mainPerInstanceBindGroupLayout (same skinMats binding)
@@ -6840,6 +6855,7 @@ export class Engine {
6840
6855
  entries: [
6841
6856
  { binding: 0, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
6842
6857
  { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float" } },
6858
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float" } },
6843
6859
  ],
6844
6860
  });
6845
6861
  const outlinePipelineLayout = this.device.createPipelineLayout({
@@ -6856,6 +6872,7 @@ export class Engine {
6856
6872
  entries: [
6857
6873
  { binding: 0, resource: { buffer: this.cameraUniformBuffer } },
6858
6874
  { binding: 1, resource: this.materialSampler },
6875
+ { binding: 2, resource: { buffer: this.lightUniformBuffer } },
6859
6876
  ],
6860
6877
  });
6861
6878
  // The same group on the MIRROR camera. An outline is geometry expanded
@@ -6868,6 +6885,7 @@ export class Engine {
6868
6885
  entries: [
6869
6886
  { binding: 0, resource: { buffer: this.mirrorCameraBuffer } },
6870
6887
  { binding: 1, resource: this.materialSampler },
6888
+ { binding: 2, resource: { buffer: this.lightUniformBuffer } },
6871
6889
  ],
6872
6890
  });
6873
6891
  const outlineShaderModule = this.device.createShaderModule({
@@ -7037,13 +7055,14 @@ export class Engine {
7037
7055
  // strength belong to the combine step.
7038
7056
  this.compositeUniformBuffer = this.device.createBuffer({
7039
7057
  label: "composite view uniforms",
7040
- // 15 × vec4f: (exposure, invGamma, _, _) · (bloom tint, intensity) ·
7058
+ // 16 × vec4f: (exposure, invGamma, _, _) · (bloom tint, intensity) ·
7041
7059
  // (bg rgb, mode) · camera right/up/forward basis for the 360 skybox ray ·
7042
7060
  // (time, _, canvas width, canvas height) for user effects · three grade
7043
7061
  // vectors (CDL offset+contrast, power+saturation, slope+flag) · camera
7044
7062
  // world position, for an effect placing itself in the scene · four
7045
- // character positions, for one that wants to respond to the cast.
7046
- size: 240,
7063
+ // character positions, for one that wants to respond to the cast · the
7064
+ // soft curve's contrast. Shaders that stop at the cast declare 15.
7065
+ size: 256,
7047
7066
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
7048
7067
  });
7049
7068
  this.dofUniformBuffer = this.device.createBuffer({
@@ -12784,6 +12803,7 @@ export class Engine {
12784
12803
  dissolve: 1,
12785
12804
  materialUniformBuffers: [],
12786
12805
  outlineUniformBuffers: [],
12806
+ outlineByMaterial: new Map(),
12787
12807
  cullModelIndex: 0,
12788
12808
  // Seeded false: the first skin-matrix upload decides it, and until then the
12789
12809
  // sphere path is the safe answer (it never culls something it should not).
@@ -13321,8 +13341,12 @@ export class Engine {
13321
13341
  // every material — on the heaviest mesh in the scene that doubles the
13322
13342
  // geometry submitted per frame to draw cartoon outlines around
13323
13343
  // architecture, which is not the look anyone is after.
13344
+ // Every character material gets its hull: the PMX edge flag draws it as
13345
+ // MMD does, and a style group whose look carries an outline draws it on
13346
+ // any material (assignDrawCallGroups sets `drawn`).
13324
13347
  let outline;
13325
- if (!inst.isStage && (mat.edgeFlag & 0x10) !== 0 && mat.edgeSize > 0) {
13348
+ const pmxEdge = (mat.edgeFlag & 0x10) !== 0 && mat.edgeSize > 0;
13349
+ if (!inst.isStage) {
13326
13350
  const materialUniformData = new Float32Array([
13327
13351
  mat.edgeColor[0],
13328
13352
  mat.edgeColor[1],
@@ -13337,25 +13361,29 @@ export class Engine {
13337
13361
  1,
13338
13362
  // The scene's width multiplier, at RZ_OUTLINE_WIDTH_OFFSET.
13339
13363
  this.outlineWidth,
13364
+ // A style graph's outline, at RZ_OUTLINE_TEXTURED_OFFSET: none yet.
13340
13365
  0,
13341
13366
  // The scene's colour override, at RZ_OUTLINE_COLOR_OFFSET.
13342
13367
  this.outlineColor[0],
13343
13368
  this.outlineColor[1],
13344
13369
  this.outlineColor[2],
13345
13370
  this.outlineColor[3],
13371
+ // The graph outline's shadow colour, at RZ_OUTLINE_SHADOW_OFFSET.
13372
+ 0,
13373
+ 0,
13374
+ 0,
13375
+ 0,
13346
13376
  ]);
13347
13377
  const outlineUniformBuffer = this.createUniformBuffer(`${prefix}outline: ${mat.name}`, materialUniformData);
13348
13378
  inst.gpuBuffers.push(outlineUniformBuffer);
13349
13379
  inst.outlineUniformBuffers.push(outlineUniformBuffer);
13350
- const outlineBindGroup = this.device.createBindGroup({
13351
- label: `${prefix}outline: ${mat.name}`,
13352
- layout: this.outlinePerMaterialBindGroupLayout,
13353
- entries: [
13354
- { binding: 0, resource: { buffer: outlineUniformBuffer } },
13355
- { binding: 1, resource: textureView },
13356
- ],
13380
+ inst.outlineByMaterial.set(mat.name, {
13381
+ buffer: outlineUniformBuffer,
13382
+ edge: materialUniformData.slice(0, 5),
13383
+ picture: textureView,
13357
13384
  });
13358
- outline = { bindGroup: outlineBindGroup };
13385
+ const outlineBindGroup = this.outlineBindGroup(`${prefix}outline: ${mat.name}`, outlineUniformBuffer, textureView, null);
13386
+ outline = { bindGroup: outlineBindGroup, pmx: pmxEdge, drawn: pmxEdge };
13359
13387
  if (!inst.outlineVertexBuffer) {
13360
13388
  const data = inst.model.getOutlineVertices();
13361
13389
  const buf = this.device.createBuffer({
@@ -14595,29 +14623,26 @@ export class Engine {
14595
14623
  py += pos.y;
14596
14624
  pz += pos.z;
14597
14625
  }
14598
- px += this.cameraTargetOffset.x;
14599
- py += this.cameraTargetOffset.y;
14600
- pz += this.cameraTargetOffset.z;
14601
14626
  const tau = this.cameraFollowSmoothing;
14627
+ const f = this.cameraFollowPos;
14602
14628
  if (tau > 0 && this.cameraFollowSeeded) {
14603
14629
  // Exponential lag toward the bone: the handheld-camera feel. The
14604
14630
  // orbit pivot trails the target and eases in, never snapping.
14605
14631
  const k = 1 - Math.exp(-deltaTime / tau);
14606
- const f = this.cameraFollowPos;
14607
14632
  f.x += (px - f.x) * k;
14608
14633
  f.y += (py - f.y) * k;
14609
14634
  f.z += (pz - f.z) * k;
14610
- this.camera.target.x = f.x;
14611
- this.camera.target.y = f.y;
14612
- this.camera.target.z = f.z;
14613
14635
  }
14614
14636
  else {
14615
- this.cameraFollowPos.setXYZ(px, py, pz);
14637
+ f.setXYZ(px, py, pz);
14616
14638
  this.cameraFollowSeeded = true;
14617
- this.camera.target.x = px;
14618
- this.camera.target.y = py;
14619
- this.camera.target.z = pz;
14620
14639
  }
14640
+ // The offset goes on AFTER the lag. A pan writes the offset (panSink),
14641
+ // and the lag is for the bone's motion: eased with it, a pan glided
14642
+ // on after the hand let go while an orbit stopped dead.
14643
+ this.camera.target.x = f.x + this.cameraTargetOffset.x;
14644
+ this.camera.target.y = f.y + this.cameraTargetOffset.y;
14645
+ this.camera.target.z = f.z + this.cameraTargetOffset.z;
14621
14646
  }
14622
14647
  }
14623
14648
  // Who holds the shot this frame. An external pose is a statement about
@@ -15578,8 +15603,46 @@ export class Engine {
15578
15603
  }
15579
15604
  // Rebind each material draw call to its (successfully-installed) group's uniform buffer,
15580
15605
  // or the zero buffer when ungrouped, then re-sort by render-class draw order.
15606
+ /** A hull's per-material group: its block, the picture, the look's tint. */
15607
+ outlineBindGroup(label, buffer, picture, tint) {
15608
+ return this.device.createBindGroup({
15609
+ label,
15610
+ layout: this.outlinePerMaterialBindGroupLayout,
15611
+ entries: [
15612
+ { binding: 0, resource: { buffer } },
15613
+ { binding: 1, resource: picture },
15614
+ { binding: 2, resource: (tint ?? this.fallbackMaterialTexture).createView() },
15615
+ ],
15616
+ });
15617
+ }
15581
15618
  assignDrawCallGroups(inst, claimed) {
15582
15619
  inst.materialToGroup.clear();
15620
+ // The hulls: a group whose look carries an outline (ShaderGraph.outline)
15621
+ // draws it in place of the PMX edge; the rest keep, or get back, their own.
15622
+ for (const [name, hull] of inst.outlineByMaterial) {
15623
+ const id = claimed.get(name);
15624
+ const install = id ? inst.styleGroups.get(id) : undefined;
15625
+ const o = install?.group.graph.outline;
15626
+ const block = o
15627
+ ? new Float32Array([o.color[0], o.color[1], o.color[2], 1, o.width])
15628
+ : hull.edge;
15629
+ this.device.queue.writeBuffer(hull.buffer, 0, block);
15630
+ this.device.queue.writeBuffer(hull.buffer, RZ_OUTLINE_TEXTURED_OFFSET, new Float32Array([o ? 1 : 0]));
15631
+ if (o)
15632
+ this.device.queue.writeBuffer(hull.buffer, RZ_OUTLINE_SHADOW_OFFSET, new Float32Array([...o.shadowColor, 1]));
15633
+ const tint = o?.tint != null ? (install?.imagesByMaterial?.[name] ?? install?.images)?.[o.tint] ?? null : null;
15634
+ for (const dc of inst.drawCalls) {
15635
+ if (dc.materialName !== name || !dc.outline)
15636
+ continue;
15637
+ dc.outline.bindGroup = this.outlineBindGroup(`outline: ${name}`, hull.buffer, hull.picture, tint);
15638
+ this.bundlesDirty = true;
15639
+ const drawn = o ? o.width > 0 : dc.outline.pmx;
15640
+ if (dc.outline.drawn !== drawn) {
15641
+ dc.outline.drawn = drawn;
15642
+ this.bundlesDirty = true;
15643
+ }
15644
+ }
15645
+ }
15583
15646
  for (const dc of inst.drawCalls) {
15584
15647
  if (!dc.baseBindGroupEntries)
15585
15648
  continue; // outlines/ground are never grouped
@@ -16030,7 +16093,7 @@ export class Engine {
16030
16093
  }
16031
16094
  pass.setBindGroup(2, draw.bindGroup);
16032
16095
  this.issueDraw(pass, draw, view.args);
16033
- if (draw.outline && this.outlineEnabled && view.outlines)
16096
+ if (draw.outline?.drawn && this.outlineEnabled && view.outlines)
16034
16097
  outlined.push(draw);
16035
16098
  }
16036
16099
  // THE HULLS AFTER THE SURFACES, as MMD draws a model's edges. A hull held
@@ -21,7 +21,7 @@ export const BODY_GRAPH = {
21
21
  { id: "lit_tint", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.5, value: 1.0, fac: 1.0 } },
22
22
  { id: "toon_color", type: "mix/blend" },
23
23
  { id: "bc", type: "bright_contrast", inputs: { bright: 0.1, contrast: 0.2 } },
24
- { id: "emission3", type: "vector_math/scale", inputs: { scale: 4.0 } },
24
+ { id: "emission3", type: "vector_math/scale", inputs: { scale: 3.2 } },
25
25
  { id: "warm_add", type: "math/add", inputs: { b: 0.5 } },
26
26
  { id: "warm_clamp", type: "math/clamp01" },
27
27
  {
@@ -27,7 +27,7 @@ export const CLOTH_ROUGH_GRAPH = {
27
27
  { id: "mix_003", type: "mix/blend" },
28
28
  { id: "hue_004", type: "hue_sat", inputs: { hue: 0.5, saturation: 0.800000011920929, value: 2.0, fac: 1.0 } },
29
29
  { id: "npr_overlay", type: "mix/overlay", inputs: { fac: 1.0 } },
30
- { id: "npr_emit", type: "vector_math/scale", inputs: { scale: 18.200000762939453 } },
30
+ { id: "npr_emit", type: "vector_math/scale", inputs: { scale: 14.56 } },
31
31
  { id: "noise", type: "tex_noise", inputs: { scale: 17.7 } },
32
32
  { id: "noise_ramp", type: "ramp_linear", inputs: { pos0: 0.0, pos1: 1.0 } },
33
33
  { id: "bump", type: "bump", inputs: { strength: 1.0 } },
@@ -25,7 +25,7 @@ export const CLOTH_SMOOTH_GRAPH = {
25
25
  { id: "mix_003", type: "mix/blend" },
26
26
  { id: "hue_004", type: "hue_sat", inputs: { hue: 0.5, saturation: 0.800000011920929, value: 2.0, fac: 1.0 } },
27
27
  { id: "npr_overlay", type: "mix/overlay", inputs: { fac: 1.0 } },
28
- { id: "npr_emit", type: "vector_math/scale", inputs: { scale: 18.200000762939453 } },
28
+ { id: "npr_emit", type: "vector_math/scale", inputs: { scale: 14.56 } },
29
29
  { id: "principled_base", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.0, value: 0.800000011920929, fac: 1.0 } },
30
30
  {
31
31
  id: "principled",
@@ -25,7 +25,7 @@ export const FACE_GRAPH = {
25
25
  { id: "lit_tint", type: "hue_sat", inputs: { hue: 0.46000000834465027, saturation: 1.600000023841858, value: 1.5, fac: 1.0 } },
26
26
  { id: "toon_color", type: "mix/blend" },
27
27
  { id: "bc", type: "bright_contrast", inputs: { bright: 0.1, contrast: 0.2 } },
28
- { id: "emission3", type: "vector_math/scale", inputs: { scale: 2.5 } },
28
+ { id: "emission3", type: "vector_math/scale", inputs: { scale: 2.0 } },
29
29
  // ── warm rim (toon*0.5+0.5 → cardinal ramp) ──
30
30
  { id: "warm_mul", type: "math/multiply", inputs: { b: 0.5 } },
31
31
  { id: "warm_add", type: "math/add", inputs: { b: 0.5 } },
@@ -24,7 +24,7 @@ export const METAL_GRAPH = {
24
24
  { id: "mix_004", type: "mix/blend" },
25
25
  { id: "hue_004", type: "hue_sat", inputs: { hue: 0.5, saturation: 1.0, value: 2.0, fac: 1.0 } },
26
26
  { id: "npr_overlay", type: "mix/overlay", inputs: { fac: 1.0 } },
27
- { id: "npr_emit", type: "vector_math/scale", inputs: { scale: 8.100000381469727 } },
27
+ { id: "npr_emit", type: "vector_math/scale", inputs: { scale: 6.48 } },
28
28
  { id: "voro_cross", type: "vect_cross", inputs: { b: [0, 1, 0] } },
29
29
  { id: "voro", type: "tex_voronoi/color", inputs: { scale: 4.3 } },
30
30
  { id: "voro_ramp", type: "ramp_linear", inputs: { pos0: 0.0, pos1: 1.0 } },
@@ -0,0 +1,3 @@
1
+ import type { ShaderGraph } from "../schema";
2
+ export declare const POOL_FLOOR_GRAPH: ShaderGraph;
3
+ //# sourceMappingURL=pool_floor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool_floor.d.ts","sourceRoot":"","sources":["../../../src/graph/presets/pool_floor.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,eAAO,MAAM,gBAAgB,EAAE,WA6B9B,CAAA"}
@@ -0,0 +1,42 @@
1
+ // Pool floor — the tiled bottom seen through water, with caustics on it.
2
+ //
3
+ // The other half of a pool, and the half that does most of the work. A water
4
+ // surface can be tuned for a long time without the shot reading as a pool;
5
+ // putting the focused-light web on the FLOOR is what settles it, because that
6
+ // is the cue a photograph actually carries. It is a floor material, so it is
7
+ // applied to the tile under the water rather than to the water itself.
8
+ //
9
+ // Caustics land between the albedo and the shading deliberately: they are
10
+ // light, so they should still be dimmed by the floor's own shadow rather than
11
+ // glowing through it. That is the difference between water-focused sun and a
12
+ // decal.
13
+ export const POOL_FLOOR_GRAPH = {
14
+ version: 1,
15
+ name: "Pool floor",
16
+ tags: ["stage", "water", "pool"],
17
+ nodes: [
18
+ { id: "tex", type: "texture" },
19
+ { id: "mat", type: "material_diffuse" },
20
+ { id: "base", type: "mix/multiply", inputs: { fac: 1.0 } },
21
+ { id: "caust", type: "caustics" },
22
+ {
23
+ id: "principled",
24
+ type: "principled",
25
+ inputs: { metallic: 0.0, specular_ior_level: 0.4, roughness: 0.45, spec_clamp: 10.0, sheen_weight: 0.0, sheen_tint: 0.0 },
26
+ },
27
+ ],
28
+ links: [
29
+ { from: { node: "tex", socket: "color" }, to: { node: "base", socket: "a" } },
30
+ { from: { node: "mat", socket: "color" }, to: { node: "base", socket: "b" } },
31
+ { from: { node: "base", socket: "color" }, to: { node: "caust", socket: "color" } },
32
+ { from: { node: "caust", socket: "color" }, to: { node: "principled", socket: "base_color" } },
33
+ ],
34
+ output: { node: "principled", socket: "color" },
35
+ params: [
36
+ { id: "tint", label: "Light tint", target: { node: "caust", socket: "tint" }, kind: "color", default: [1.0, 1.0, 1.0] },
37
+ { id: "scale", label: "Caustic scale", target: { node: "caust", socket: "scale" }, kind: "float", min: 0.01, max: 1, default: 0.12 },
38
+ { id: "speed", label: "Caustic speed", target: { node: "caust", socket: "speed" }, kind: "float", min: 0, max: 1, default: 0.15 },
39
+ { id: "strength", label: "Caustic strength", target: { node: "caust", socket: "strength" }, kind: "float", min: 0, max: 3, default: 0.6 },
40
+ { id: "fade", label: "Depth fade", target: { node: "caust", socket: "fade" }, kind: "float", min: 1, max: 200, default: 30 },
41
+ ],
42
+ };
@@ -0,0 +1,3 @@
1
+ import type { ShaderGraph } from "../schema";
2
+ export declare const WATER_GRAPH: ShaderGraph;
3
+ //# sourceMappingURL=water.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"water.d.ts","sourceRoot":"","sources":["../../../src/graph/presets/water.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,eAAO,MAAM,WAAW,EAAE,WAgBzB,CAAA"}
@@ -0,0 +1,31 @@
1
+ // Water — a planar reflection, rippled, seen through Fresnel.
2
+ //
3
+ // Put a stage's water materials in a style group carrying this graph and the
4
+ // pool starts reflecting. The reflection itself is the engine's mirror pass,
5
+ // which this is the only graph to read; everything else here is the shaping
6
+ // that keeps it from reading as a mirror laid flat in the floor.
7
+ //
8
+ // The exposed params are the ones worth a slider while judging a shot. Scale
9
+ // and speed are the ripple; a real pool's surface is slow and broad, so both
10
+ // defaults sit low. Tint is the water's own colour — a game rip carries that
11
+ // in its material, and the converter hands it straight over.
12
+ //
13
+ // How see-through the water is belongs to the MATERIAL's alpha, not to this
14
+ // graph, which is where every other group's coverage comes from too.
15
+ export const WATER_GRAPH = {
16
+ version: 1,
17
+ name: "Water",
18
+ tags: ["water", "stage"],
19
+ nodes: [{ id: "water", type: "water" }],
20
+ links: [],
21
+ output: { node: "water", socket: "color" },
22
+ params: [
23
+ { id: "tint", label: "Tint", target: { node: "water", socket: "tint" }, kind: "color", default: [0.04, 0.62, 0.66] },
24
+ { id: "scale", label: "Ripple scale", target: { node: "water", socket: "scale" }, kind: "float", min: 0.01, max: 1, default: 0.18 },
25
+ { id: "speed", label: "Ripple speed", target: { node: "water", socket: "speed" }, kind: "float", min: 0, max: 1, default: 0.12 },
26
+ { id: "strength", label: "Ripple depth", target: { node: "water", socket: "strength" }, kind: "float", min: 0, max: 2, default: 0.7 },
27
+ { id: "blur", label: "Reflection blur", target: { node: "water", socket: "blur" }, kind: "float", min: 0, max: 1, default: 0.25 },
28
+ { id: "mirroring", label: "Mirroring", target: { node: "water", socket: "mirroring" }, kind: "float", min: 0, max: 1, default: 0.20 },
29
+ { id: "sparkle", label: "Sparkle", target: { node: "water", socket: "sparkle" }, kind: "float", min: 0, max: 4, default: 1.0 },
30
+ ],
31
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/graph/registry.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEzD,KAAK,SAAS,GAAG;IACf,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC9B,2FAA2F;IAC3F,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAA;IAC/C,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC;sEACkE;IAClE,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAYD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,GAAG,MAAM,CAoBhE;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAIpE;AAMD,wBAAgB,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAM1D;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKpE;AA4GD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA+yBlD,CAAA"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/graph/registry.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEzD,KAAK,SAAS,GAAG;IACf,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC9B,2FAA2F;IAC3F,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAA;IAC/C,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC;sEACkE;IAClE,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAYD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,GAAG,MAAM,CAoBhE;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAIpE;AAMD,wBAAgB,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAM1D;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKpE;AAsLD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAozBlD,CAAA"}
@@ -101,7 +101,11 @@ const uberNode = (slot) => ({
101
101
  roughness: F(0.6),
102
102
  occlusion: F(1),
103
103
  normal: { type: "vector", contextDefault: "n" },
104
- direction: { type: "vector", requiresLink: true },
104
+ // The key light: this draw's, by default — the sun, or the light on the
105
+ // character's own layer — its way and its colour (the game's _LocalLightDir,
106
+ // _LocalLightColor). Linked, a look supplies its own (ag_key_light).
107
+ direction: { type: "vector", contextDefault: "l" },
108
+ light_color: { type: "color", contextDefault: "sun" },
105
109
  // Unity's v into the ramp: the property map's alpha, which picks a
106
110
  // surface's band (skin 0.702, hair and pale cloth 0.506).
107
111
  row: F(0.702),
@@ -115,23 +119,44 @@ const uberNode = (slot) => ({
115
119
  rim_in_light: F(1),
116
120
  emission: C([0, 0, 0]),
117
121
  reflection: F(1),
122
+ // THE STUDIO AMBIENT (_GlobalIlluminationOverride): the game blends a
123
+ // character's ambient from its own reflection cube's smallest mip — near
124
+ // neutral, 0.4–0.9 across the newer skins — over the scene's light, by the
125
+ // material's blend (bodies 0.8, hair 1, faces 0.5). It is what keeps a
126
+ // character clean on a coloured stage.
127
+ studio: C([0.6, 0.6, 0.6]),
128
+ studio_blend: F(0),
118
129
  // A face's SDF shade (ag_face_sdf); left at -1, the material reads N·L.
119
130
  shade: F(-1),
120
131
  // 1: the game's FACE_MODE — no GGX, the environment at a flat 0.0157.
121
132
  face: F(0),
133
+ // A face's SDF highlight (ag_face_spec), in place of GGX.
134
+ face_spec: F(0),
135
+ // 1: hair (_ANISOTROPIC_SPECULAR) — its ring (ag_hair_ring) in place of GGX,
136
+ // the environment at a flat 0.0157.
137
+ hair: F(0),
138
+ hair_spec: C([0, 0, 0]),
122
139
  },
123
140
  outputs: { color: "color" },
124
- emit: (a) => `ag_uber(${slot}u, ${a.base}, ${a.metallic}, ${a.roughness}, ${a.occlusion}, ${a.normal}, ${a.direction}, ${a.row}, ${a.receive_shadow}, ` +
125
- `${a.rim_mask}, ${a.rim_mid}, ${a.rim_width}, ${a.rim_tint}, ${a.rim_intensity}, ${a.rim_albedo}, ${a.rim_in_light}, ${a.emission}, ${a.reflection}, ${a.shade}, ${a.face}, input.worldPos, v)`,
141
+ emit: (a) => `ag_uber(${slot}u, ${a.base}, ${a.metallic}, ${a.roughness}, ${a.occlusion}, ${a.normal}, ${a.direction}, ${a.light_color}, ${a.row}, ${a.receive_shadow}, ` +
142
+ `${a.rim_mask}, ${a.rim_mid}, ${a.rim_width}, ${a.rim_tint}, ${a.rim_intensity}, ${a.rim_albedo}, ${a.rim_in_light}, ${a.emission}, ${a.reflection}, ${a.studio}, ${a.studio_blend}, ${a.shade}, ${a.face}, ${a.face_spec}, ${a.hair}, ${a.hair_spec}, input.worldPos, v)`,
126
143
  takesLight: true,
127
144
  });
128
145
  const UBER_NODES = Object.fromEntries([0, 1, 2, 3].map((s) => [`ag_uber/${s}`, uberNode(s)]));
129
- // The face's SDF shade, the image on the slot the type names — 104903's face
130
- // template (SDF_04_shaonv) by default in the AG pack.
146
+ // How much a colour reads as skin (ag_skin): what picks a surface's ramp row in
147
+ // a general look, where the game would read its property mask.
148
+ const AG_SKIN = {
149
+ inputs: { color: C([1, 1, 1], true) },
150
+ outputs: { value: "float" },
151
+ emit: (a) => `ag_skin(${a.color})`,
152
+ };
153
+ // The face's SDF shade, the image on the slot the type names. An empty slot
154
+ // reads white: the whole face takes one shade from where the key light stands
155
+ // to the head, the game's flat face for a model without its SDF.
131
156
  const faceSdfNode = (slot) => ({
132
157
  inputs: {
133
158
  uv: { type: "vector", contextDefault: "vec3f(input.uv, 0.0)" },
134
- direction: { type: "vector", requiresLink: true },
159
+ direction: { type: "vector", contextDefault: "l" },
135
160
  normal: { type: "vector", contextDefault: "n" },
136
161
  smoothness: F(0.1),
137
162
  invert: F(0),
@@ -140,6 +165,54 @@ const faceSdfNode = (slot) => ({
140
165
  emit: (a) => `ag_face_sdf(${slot}u, ${a.uv}.xy, ${a.direction}, ${a.normal}, ${a.smoothness}, ${a.invert})`,
141
166
  });
142
167
  const FACE_SDF_NODES = Object.fromEntries([0, 1, 2, 3].map((s) => [`ag_face_sdf/${s}`, faceSdfNode(s)]));
168
+ // The newer SDF (_SDFType 1): a painted face normal, not a threshold field.
169
+ const faceSdfNewNode = (slot) => ({
170
+ ...faceSdfNode(slot),
171
+ emit: (a) => `ag_face_sdf_new(${slot}u, ${a.uv}.xy, ${a.direction}, ${a.normal}, ${a.smoothness}, ${a.invert})`,
172
+ });
173
+ const FACE_SDF_NEW_NODES = Object.fromEntries([0, 1, 2, 3].map((s) => [`ag_face_sdf_new/${s}`, faceSdfNewNode(s)]));
174
+ // The face's highlight from the newer SDF's green and blue.
175
+ const faceSpecNode = (slot) => ({
176
+ inputs: {
177
+ uv: { type: "vector", contextDefault: "vec3f(input.uv, 0.0)" },
178
+ direction: { type: "vector", contextDefault: "l" },
179
+ normal: { type: "vector", contextDefault: "n" },
180
+ anisotropy: F(1),
181
+ shift: F(0),
182
+ },
183
+ outputs: { value: "float" },
184
+ emit: (a) => `ag_face_spec(${slot}u, ${a.uv}.xy, ${a.direction}, ${a.normal}, v, ${a.anisotropy}, ${a.shift})`,
185
+ });
186
+ // The hair's angel ring, the band on the slot's image.
187
+ const hairRingNode = (slot) => ({
188
+ inputs: {
189
+ uv: { type: "vector", contextDefault: "vec3f(input.uv, 0.0)" },
190
+ normal: { type: "vector", contextDefault: "n" },
191
+ color: C([1, 1, 1]),
192
+ anisotropy: F(1),
193
+ shift: F(0),
194
+ },
195
+ outputs: { color: "color" },
196
+ emit: (a) => `ag_hair_ring(${slot}u, ${a.uv}.xy, ${a.normal}, v, ${a.color}, ${a.anisotropy}, ${a.shift})`,
197
+ });
198
+ const HAIR_RING_NODES = Object.fromEntries([0, 1, 2, 3].map((s) => [`ag_hair_ring/${s}`, hairRingNode(s)]));
199
+ // The game's pupil (SimPipeline/Character/Eye): unlit relief, matcap and
200
+ // glints — slots: 0 depth, 1 normal, 2 matcap, 3 glint mask.
201
+ const AG_EYE = {
202
+ inputs: {
203
+ height_scale: F(0.2), min_layer: F(8), max_layer: F(32), intensity: F(0.9),
204
+ main_color: C([1, 1, 1]), matcap_color: C([1, 1, 1]), matcap_pow: F(1),
205
+ mask_scale: F(0.15), mask_soft: F(0.014),
206
+ mask2: F(0), mask2_color: C([1, 1, 1]), mask3: F(0), mask3_color: C([1, 1, 1]),
207
+ blink: F(0), blink_scale: F(10), scale_speed: F(0.1), rotate_speed: F(0.6), blink_angle: F(5),
208
+ level: F(1),
209
+ },
210
+ outputs: { color: "color" },
211
+ emit: (a) => `ag_eye(input.uv, n, v, input.worldPos, ${a.height_scale}, ${a.min_layer}, ${a.max_layer}, ${a.intensity}, ${a.main_color}, ` +
212
+ `${a.matcap_color}, ${a.matcap_pow}, ${a.mask_scale}, ${a.mask_soft}, ${a.mask2}, ${a.mask2_color}, ${a.mask3}, ${a.mask3_color}, ` +
213
+ `${a.blink}, ${a.blink_scale}, ${a.scale_speed}, ${a.rotate_speed}, ${a.blink_angle}, ${a.level})`,
214
+ };
215
+ const FACE_SPEC_NODES = Object.fromEntries([0, 1, 2, 3].map((s) => [`ag_face_spec/${s}`, faceSpecNode(s)]));
143
216
  // A packed property map on the slot the type names — the game's material
144
217
  // contract: R metal, G perceptual roughness (answered as smoothness, Unity's
145
218
  // word for 1 − r), B occlusion, A emission mask, each remapped through the
@@ -586,7 +659,12 @@ export const NODE_REGISTRY = {
586
659
  // Principled is one: the ramp image on the group slot named by the type.
587
660
  // Defaults are 104903's skin.
588
661
  ...UBER_NODES,
662
+ ag_skin: AG_SKIN,
589
663
  ...FACE_SDF_NODES,
664
+ ...FACE_SDF_NEW_NODES,
665
+ ...FACE_SPEC_NODES,
666
+ ag_eye: AG_EYE,
667
+ ...HAIR_RING_NODES,
590
668
  ...PROPERTY_MAP_NODES,
591
669
  /**
592
670
  * Lit — Unity's Lit (URP's PBR), term for term: what a stage, a prop or
@@ -89,6 +89,20 @@ export type ShaderGraph = {
89
89
  url: string;
90
90
  srgb?: boolean;
91
91
  } | null)[];
92
+ /**
93
+ * The look's outline, the anime game's in place of MMD's flat edge: the
94
+ * material's picture × colour, toward shadowColor where the key light leaves
95
+ * it, `width` world units wide (held 1.2–3 px of a 1920-wide frame) — an Aether
96
+ * Gazer _OutlineWidth w is w·1.3/1920·8 here. Unset, the PMX edge stands.
97
+ */
98
+ outline?: {
99
+ color: [number, number, number];
100
+ shadowColor: [number, number, number];
101
+ width: number;
102
+ /** One of `images` (its index) whose colour multiplies the outline at the
103
+ * mesh's uvs — the game's per-texel outline mask. */
104
+ tint?: number;
105
+ };
92
106
  };
93
107
  export type Diagnostic = {
94
108
  severity: "error" | "warning";
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/graph/schema.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAE9F,MAAM,MAAM,SAAS,GAAG;IACtB,kFAAkF;IAClF,EAAE,EAAE,MAAM,CAAA;IACV,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAA;IACZ,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IACpC;mFAC+E;IAC/E,EAAE,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAC7C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CACrC,CAAA;AAED,0FAA0F;AAC1F,MAAM,MAAM,YAAY,GAAG;IACzB,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,WAAW,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,CAAC,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,uEAAuE;IACvE,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1C,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;IACvB;;uFAEmF;IACnF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC,EAAE,CAAA;CACpD,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAID,eAAO,MAAM,SAAS,MAAM,CAAA;AAC5B,eAAO,MAAM,UAAU,KAAK,CAAA"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/graph/schema.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAE9F,MAAM,MAAM,SAAS,GAAG;IACtB,kFAAkF;IAClF,EAAE,EAAE,MAAM,CAAA;IACV,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAA;IACZ,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IACpC;mFAC+E;IAC/E,EAAE,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAC7C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CACrC,CAAA;AAED,0FAA0F;AAC1F,MAAM,MAAM,YAAY,GAAG;IACzB,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,WAAW,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,CAAC,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,uEAAuE;IACvE,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1C,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;IACvB;;uFAEmF;IACnF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC,EAAE,CAAA;IACnD;;;;;OAKG;IACH,OAAO,CAAC,EAAE;QACR,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/B,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QACrC,KAAK,EAAE,MAAM,CAAA;QACb;8DACsD;QACtD,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;CACF,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAID,eAAO,MAAM,SAAS,MAAM,CAAA;AAC5B,eAAO,MAAM,UAAU,KAAK,CAAA"}