@call-me-sensei/toonlab 0.1.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 (146) hide show
  1. package/ATTRIBUTION.md +40 -0
  2. package/LICENSE +21 -0
  3. package/README.md +125 -0
  4. package/package.json +97 -0
  5. package/src/character/characterRig.js +353 -0
  6. package/src/character/freestyleSwimClip.js +541 -0
  7. package/src/character/index.js +4 -0
  8. package/src/character/modelLoader.js +433 -0
  9. package/src/core/materialRoles.js +438 -0
  10. package/src/core/presetDocuments.js +110 -0
  11. package/src/core/shaderBackend.js +24 -0
  12. package/src/debrisgen/debrisFields.js +544 -0
  13. package/src/debrisgen/debrisGenerator.js +1929 -0
  14. package/src/debrisgen/debrisPalettes.js +71 -0
  15. package/src/debrisgen/debrisPhysics.js +198 -0
  16. package/src/debrisgen/debrisPresets.js +155 -0
  17. package/src/debrisgen/debrisSettings.js +333 -0
  18. package/src/debrisgen/debrisTextures.js +380 -0
  19. package/src/debrisgen/index.js +5 -0
  20. package/src/debug/fieldValues.js +95 -0
  21. package/src/debug/index.js +7 -0
  22. package/src/debug/settingsPanel.js +170 -0
  23. package/src/environment/environmentAmbientProbe.js +160 -0
  24. package/src/environment/environmentMaterialAdapter.js +336 -0
  25. package/src/environment/environmentMaterialClassifier.js +178 -0
  26. package/src/environment/environmentPlanarReflection.js +201 -0
  27. package/src/environment/environmentPresets.js +443 -0
  28. package/src/environment/environmentRigs.js +684 -0
  29. package/src/environment/environmentSettings.js +504 -0
  30. package/src/environment/environmentShaderMaterials.js +318 -0
  31. package/src/environment/environmentSunShadowPass.js +268 -0
  32. package/src/environment/environmentTextureResolver.js +161 -0
  33. package/src/environment/environmentTimeOfDay.js +167 -0
  34. package/src/environment/environmentVertexAo.js +188 -0
  35. package/src/environment/index.js +8 -0
  36. package/src/environment/scanAssetStylize.js +103 -0
  37. package/src/index.js +21 -0
  38. package/src/loaders/index.js +1 -0
  39. package/src/post/index.js +2 -0
  40. package/src/post/postProcessing.js +1087 -0
  41. package/src/rockgen/export/glbExport.js +177 -0
  42. package/src/rockgen/heightfield/heightfieldErosion.js +7 -0
  43. package/src/rockgen/heightfield/heightfieldPatch.js +183 -0
  44. package/src/rockgen/heightfield/stylizedErosionSim.js +356 -0
  45. package/src/rockgen/index.js +14 -0
  46. package/src/rockgen/mesh/meshAttributes.js +374 -0
  47. package/src/rockgen/mesh/meshDocument.js +158 -0
  48. package/src/rockgen/mesh/surfaceNets.js +329 -0
  49. package/src/rockgen/noise/cellularNoise3.js +92 -0
  50. package/src/rockgen/noise/prng.js +62 -0
  51. package/src/rockgen/noise/simplexNoise3.js +89 -0
  52. package/src/rockgen/noise/valueNoise3.js +87 -0
  53. package/src/rockgen/rockDocument.js +255 -0
  54. package/src/rockgen/rockHelpers.js +8 -0
  55. package/src/rockgen/rockgenPresets.js +559 -0
  56. package/src/rockgen/rockgenSettings.js +756 -0
  57. package/src/rockgen/sdf/fieldCompiler.js +544 -0
  58. package/src/rockgen/sdf/sculptEdits.js +66 -0
  59. package/src/rockgen/sdf/sdfModifiers.js +45 -0
  60. package/src/rockgen/sdf/sdfOps.js +37 -0
  61. package/src/rockgen/sdf/sdfPrimitives.js +88 -0
  62. package/src/shaders-tsl/anime.js +963 -0
  63. package/src/shaders-tsl/chunks/character-color.js +64 -0
  64. package/src/shaders-tsl/chunks/character-highlights.js +174 -0
  65. package/src/shaders-tsl/chunks/character-lighting.js +309 -0
  66. package/src/shaders-tsl/chunks/character-material-maps.js +146 -0
  67. package/src/shaders-tsl/chunks/character-roles.js +52 -0
  68. package/src/shaders-tsl/chunks/character-scene-lights.js +270 -0
  69. package/src/shaders-tsl/chunks/character-shadow-color.js +61 -0
  70. package/src/shaders-tsl/chunks/character-skinning.js +140 -0
  71. package/src/shaders-tsl/chunks/environment-color.js +47 -0
  72. package/src/shaders-tsl/chunks/environment-debug.js +79 -0
  73. package/src/shaders-tsl/chunks/environment-lighting.js +260 -0
  74. package/src/shaders-tsl/chunks/environment-sun-shadow.js +87 -0
  75. package/src/shaders-tsl/chunks/foliage-fog.js +60 -0
  76. package/src/shaders-tsl/chunks/pass-depth-color.js +89 -0
  77. package/src/shaders-tsl/chunks/stylized-cloud-shadow.js +70 -0
  78. package/src/shaders-tsl/chunks/water-color.js +190 -0
  79. package/src/shaders-tsl/chunks/water-common.js +124 -0
  80. package/src/shaders-tsl/chunks/water-foam.js +93 -0
  81. package/src/shaders-tsl/chunks/water-lighting.js +116 -0
  82. package/src/shaders-tsl/chunks/water-ripple.js +64 -0
  83. package/src/shaders-tsl/chunks/water-waves.js +100 -0
  84. package/src/shaders-tsl/environment-ao-overlay.js +60 -0
  85. package/src/shaders-tsl/environment.js +680 -0
  86. package/src/shaders-tsl/flower.js +267 -0
  87. package/src/shaders-tsl/grass.js +193 -0
  88. package/src/shaders-tsl/post-composite.js +461 -0
  89. package/src/shaders-tsl/sky.js +175 -0
  90. package/src/shaders-tsl/tree-leaf.js +264 -0
  91. package/src/shaders-tsl/water-breaker.js +423 -0
  92. package/src/shaders-tsl/water-kelp.js +171 -0
  93. package/src/shaders-tsl/water-rain.js +107 -0
  94. package/src/shaders-tsl/water-simulation.js +140 -0
  95. package/src/shaders-tsl/water-splash.js +298 -0
  96. package/src/shaders-tsl/water.js +509 -0
  97. package/src/sky/index.js +2 -0
  98. package/src/sky/stylizedSky.js +341 -0
  99. package/src/toon/characterRenderPasses.js +646 -0
  100. package/src/toon/index.js +4 -0
  101. package/src/toon/settings/alphaSettings.js +170 -0
  102. package/src/toon/settings/averageShadowSettings.js +181 -0
  103. package/src/toon/settings/baseTextureSettings.js +133 -0
  104. package/src/toon/settings/celShadeSettings.js +96 -0
  105. package/src/toon/settings/contactShadowSettings.js +114 -0
  106. package/src/toon/settings/eyeHighlightSettings.js +187 -0
  107. package/src/toon/settings/faceLightingSettings.js +135 -0
  108. package/src/toon/settings/furSettings.js +112 -0
  109. package/src/toon/settings/glitterSettings.js +102 -0
  110. package/src/toon/settings/hairHighlightSettings.js +325 -0
  111. package/src/toon/settings/indirectLightSettings.js +244 -0
  112. package/src/toon/settings/localLightSettings.js +176 -0
  113. package/src/toon/settings/materialMapSettings.js +299 -0
  114. package/src/toon/settings/outlineSettings.js +263 -0
  115. package/src/toon/settings/perspectiveRemovalSettings.js +60 -0
  116. package/src/toon/settings/rimLightSettings.js +268 -0
  117. package/src/toon/settings/sceneShadowSettings.js +147 -0
  118. package/src/toon/settings/selfShadowSettings.js +221 -0
  119. package/src/toon/settings/shadowColorSettings.js +238 -0
  120. package/src/toon/settings/skinToneSettings.js +150 -0
  121. package/src/toon/settings/specularSettings.js +314 -0
  122. package/src/toon/settings/stickerSettings.js +102 -0
  123. package/src/toon/toonMaterialAdapter.js +2235 -0
  124. package/src/toon/toonSettings.js +860 -0
  125. package/src/vegetation/flowerSpecies.js +260 -0
  126. package/src/vegetation/index.js +10 -0
  127. package/src/vegetation/stylizedBush.js +136 -0
  128. package/src/vegetation/stylizedFlower.js +213 -0
  129. package/src/vegetation/stylizedFlowers.js +297 -0
  130. package/src/vegetation/stylizedGrass.js +491 -0
  131. package/src/vegetation/stylizedTree.js +2449 -0
  132. package/src/vegetation/stylizedTreeFoliage.js +691 -0
  133. package/src/vegetation/treeExport.js +300 -0
  134. package/src/vegetation/treeRecipe.js +647 -0
  135. package/src/water/index.js +2 -0
  136. package/src/water/water.js +20 -0
  137. package/src/water/waterBreakerSystem.js +591 -0
  138. package/src/water/waterInteraction.js +193 -0
  139. package/src/water/waterMaterial.js +263 -0
  140. package/src/water/waterRain.js +136 -0
  141. package/src/water/waterRippleSimulation.js +227 -0
  142. package/src/water/waterScenePasses.js +415 -0
  143. package/src/water/waterSettings.js +1138 -0
  144. package/src/water/waterSplashSystem.js +251 -0
  145. package/src/water/waterSurface.js +475 -0
  146. package/src/water/waterVegetation.js +83 -0
@@ -0,0 +1,559 @@
1
+ // Named rockgen presets: one string selects a coherent rock look (piece
2
+ // settings partials, plus optional document-level surface/meshing
3
+ // overrides). Registry mirrors environmentPresets.js. Phase B adds the
4
+ // cliff/mountain presets; Phase C adds multi-piece `kind: 'document'`
5
+ // presets (mountain ranges, scree clusters).
6
+
7
+ /** Document type tag for shareable rockgen preset JSON documents. */
8
+ export const ROCKGEN_PRESET_DOCUMENT_TYPE = 'toonlab/rockgen-preset';
9
+
10
+ /** Current schema version for rockgen preset documents. */
11
+ export const ROCKGEN_PRESET_SCHEMA_VERSION = 1;
12
+
13
+ const ROCKGEN_PRESETS = new Map();
14
+
15
+ export function registerRockgenPreset(name, preset, { overwrite = false } = {}) {
16
+ const key = String(name ?? '').trim();
17
+ if (!key) throw new Error('Rockgen preset name is required.');
18
+ if (!overwrite && ROCKGEN_PRESETS.has(key)) {
19
+ throw new Error(`Rockgen preset "${key}" is already registered.`);
20
+ }
21
+ ROCKGEN_PRESETS.set(key, {
22
+ kind: 'piece', // 'document' presets carry a `pieces` array instead
23
+ label: key,
24
+ meshing: null,
25
+ piece: {},
26
+ pieces: null,
27
+ surface: null,
28
+ ...preset,
29
+ });
30
+ return key;
31
+ }
32
+
33
+ export function normalizeRockgenPresetName(name) {
34
+ const key = String(name ?? 'boulder').trim();
35
+ return ROCKGEN_PRESETS.has(key) ? key : 'boulder';
36
+ }
37
+
38
+ export function getRockgenPresetOptions(kind = null) {
39
+ return Array.from(ROCKGEN_PRESETS.entries())
40
+ .filter(([, preset]) => kind === null || preset.kind === kind)
41
+ .map(([value, preset]) => ({ label: preset.label, value }));
42
+ }
43
+
44
+ /**
45
+ * Returns a deep copy of the preset (`{ kind, label, piece, surface,
46
+ * meshing }`) safe to mutate; unknown names fall back to 'boulder'.
47
+ */
48
+ export function resolveRockgenPreset(name) {
49
+ return structuredClone(ROCKGEN_PRESETS.get(normalizeRockgenPresetName(name)));
50
+ }
51
+
52
+ // Preset doctrine (learned the hard way): rocks are flat or round with
53
+ // some jagged sides. Jaggedness comes from planar cuts; facet grooves and
54
+ // domain warp past ~0.15 read as melted organic tissue, and gradient
55
+ // normals on blobby geometry amplify it. Round presets stay smooth and
56
+ // QUIET (river stones); everything else gets flat normals + cuts.
57
+
58
+ registerRockgenPreset('boulder', {
59
+ label: 'Boulder',
60
+ meshing: { normalsMode: 'flat' },
61
+ piece: {
62
+ cuts: {
63
+ bevel: 0.03, count: 7, depth: 0.24, enabled: true, verticalBias: 0.3,
64
+ },
65
+ falloff: { bottomFlatten: 0.4 },
66
+ name: 'Boulder',
67
+ noise: { amplitude: 0.07, frequency: 1.1, octaves: 3 },
68
+ shape: { sizeX: 1.15, sizeY: 0.8, sizeZ: 1.0 },
69
+ warp: { frequency: 0.7, strength: 0.08 },
70
+ },
71
+ });
72
+
73
+ registerRockgenPreset('river-boulder', {
74
+ label: 'River Boulder',
75
+ piece: {
76
+ falloff: { bottomFlatten: 0.2 },
77
+ name: 'River Boulder',
78
+ // Water-worn = genuinely smooth: one gentle noise octave pair, a
79
+ // whisper of warp, gradient normals. The one preset meant to be round.
80
+ noise: { amplitude: 0.05, frequency: 0.8, octaves: 2 },
81
+ shape: { sizeX: 1.1, sizeY: 0.7, sizeZ: 0.9 },
82
+ warp: { frequency: 0.6, strength: 0.12 },
83
+ },
84
+ surface: {
85
+ baseColor: [0.58, 0.57, 0.55],
86
+ cavityColor: [0.4, 0.38, 0.35],
87
+ topColor: [0.82, 0.84, 0.85],
88
+ },
89
+ });
90
+
91
+ registerRockgenPreset('karst-spire', {
92
+ label: 'Karst Spire',
93
+ meshing: { normalsMode: 'flat' },
94
+ piece: {
95
+ cuts: {
96
+ bevel: 0.02, count: 8, depth: 0.3, enabled: true, verticalBias: 0.6,
97
+ },
98
+ falloff: { bottomFlatten: 0.35, topTaper: 0.5 },
99
+ name: 'Karst Spire',
100
+ noise: {
101
+ amplitude: 0.07, frequency: 2.0, octaves: 4, ridged: true,
102
+ },
103
+ shape: { capsuleLength: 3.4, sizeX: 0.75, type: 'capsule' },
104
+ strata: {
105
+ enabled: true, frequency: 2.2, sharpness: 0.6, strength: 0.07, tiltDegrees: 4,
106
+ },
107
+ warp: { enabled: false, frequency: 0.55, strength: 0.1 },
108
+ },
109
+ surface: {
110
+ baseColor: [0.6, 0.58, 0.52],
111
+ cavityColor: [0.35, 0.32, 0.27],
112
+ topColor: [0.78, 0.82, 0.8],
113
+ topSlopeStart: 0.75,
114
+ },
115
+ });
116
+
117
+ registerRockgenPreset('sea-stack', {
118
+ label: 'Sea Stack',
119
+ meshing: { normalsMode: 'flat' },
120
+ piece: {
121
+ cuts: {
122
+ bevel: 0.025, count: 8, depth: 0.3, enabled: true, verticalBias: 0.75,
123
+ },
124
+ falloff: { bottomFlatten: 0.5, radialPinch: 0.25 },
125
+ name: 'Sea Stack',
126
+ noise: { amplitude: 0.06, frequency: 1.4, octaves: 3 },
127
+ shape: { capsuleLength: 2.4, sizeX: 0.95, type: 'capsule' },
128
+ strata: {
129
+ enabled: true, frequency: 3.6, sharpness: 0.7, strength: 0.1, tiltDegrees: 6,
130
+ },
131
+ warp: { enabled: false, frequency: 0.6, strength: 0.1 },
132
+ },
133
+ surface: {
134
+ baseColor: [0.45, 0.44, 0.42],
135
+ cavityColor: [0.26, 0.25, 0.23],
136
+ topColor: [0.75, 0.78, 0.76],
137
+ topHeightStart: 0.65,
138
+ },
139
+ });
140
+
141
+ // --- Showcase presets: tuned looks meant to sell the generator ------------
142
+ // Flat normals + strong Voronoi faceting is the signature stylized-rock
143
+ // recipe: surface nets' even topology turns into clean angular planes that
144
+ // cel-shade beautifully.
145
+
146
+ registerRockgenPreset('granite-boulder', {
147
+ label: 'Granite Block (Angular)',
148
+ meshing: { normalsMode: 'flat' },
149
+ piece: {
150
+ // Angular granite = many all-direction planar cuts on a squat
151
+ // ellipsoid. (The old facet-groove version read as a brain.)
152
+ cracks: {
153
+ coverage: 0.45, depth: 0.06, enabled: true, scale: 1.1, width: 0.07,
154
+ },
155
+ cuts: {
156
+ bevel: 0.02, count: 11, depth: 0.3, enabled: true, verticalBias: 0.2,
157
+ },
158
+ falloff: { bottomFlatten: 0.45 },
159
+ name: 'Granite Block',
160
+ noise: {
161
+ amplitude: 0.05, frequency: 1.3, octaves: 3,
162
+ },
163
+ shape: { sizeX: 1.15, sizeY: 0.85, sizeZ: 1.0 },
164
+ warp: { frequency: 0.7, strength: 0.06 },
165
+ },
166
+ surface: {
167
+ baseColor: [0.55, 0.54, 0.53],
168
+ cavityColor: [0.3, 0.29, 0.3],
169
+ colorNoise: 0.04,
170
+ topColor: [0.78, 0.79, 0.77],
171
+ topHeightStart: 0.45,
172
+ topSlopeStart: 0.6,
173
+ },
174
+ });
175
+
176
+ // Columnar basalt cliff (Liyue / Giant's Causeway): vertical prismatic
177
+ // columns with stepped tops and offset horizontal joints. The columns
178
+ // stage defines the structure; strata adds the per-column break lines.
179
+ registerRockgenPreset('basalt-columns', {
180
+ label: 'Basalt Columns',
181
+ meshing: { exportResolution: 256, normalsMode: 'flat', previewResolution: 96 },
182
+ piece: {
183
+ columns: {
184
+ enabled: true, grooveDepth: 0.17, grooveWidth: 0.13, heightVariation: 1.2, scale: 1.7,
185
+ },
186
+ falloff: { bottomFlatten: 0.35 },
187
+ name: 'Basalt Columns',
188
+ noise: { amplitude: 0.035, frequency: 2.4, octaves: 3 },
189
+ shape: {
190
+ cornerRadius: 0.2, sizeX: 1.6, sizeY: 2.4, sizeZ: 1.1, type: 'box',
191
+ },
192
+ strata: {
193
+ enabled: true, frequency: 2.0, sharpness: 0.9, strength: 0.04, tiltDegrees: 0, warpAmount: 0.05,
194
+ },
195
+ warp: { frequency: 0.4, strength: 0.08 },
196
+ },
197
+ surface: {
198
+ baseColor: [0.68, 0.58, 0.48],
199
+ cavityColor: [0.4, 0.31, 0.24],
200
+ colorNoise: 0.05,
201
+ topColor: [0.8, 0.73, 0.62],
202
+ topHeightStart: 0.3,
203
+ topSlopeStart: 0.6,
204
+ },
205
+ });
206
+
207
+ // A single sheer cliff wall: near-raw box silhouette, planar cuts with a
208
+ // strong vertical bias for stepped rock faces, whisper-quiet noise so the
209
+ // faces stay planar, subtle strata joints. Warp stays OFF — it would bow
210
+ // the wall back into a blob.
211
+ registerRockgenPreset('cliff-wall', {
212
+ label: 'Cliff Wall (Flat)',
213
+ meshing: { exportResolution: 288, normalsMode: 'flat', previewResolution: 96 },
214
+ piece: {
215
+ // Columns give the stepped skyline and vertical joints; cuts stagger
216
+ // the wall plane itself so the face isn't one flat sheet.
217
+ columns: {
218
+ enabled: true, grooveDepth: 0.1, grooveWidth: 0.14, heightVariation: 0.9, scale: 1.2,
219
+ },
220
+ cuts: {
221
+ bevel: 0.02, count: 10, depth: 0.32, enabled: true, verticalBias: 0.85,
222
+ },
223
+ falloff: { bottomFlatten: 0.45 },
224
+ name: 'Cliff Wall',
225
+ noise: { amplitude: 0.03, frequency: 2.2, octaves: 3 },
226
+ shape: {
227
+ cornerRadius: 0.02, sizeX: 2.6, sizeY: 2.4, sizeZ: 1.0, type: 'box',
228
+ },
229
+ strata: {
230
+ enabled: true, frequency: 1.6, sharpness: 0.85, strength: 0.045, tiltDegrees: 2, warpAmount: 0.08,
231
+ },
232
+ warp: { enabled: false, frequency: 0.4, strength: 0.08 },
233
+ },
234
+ surface: {
235
+ baseColor: [0.68, 0.58, 0.48],
236
+ cavityColor: [0.4, 0.31, 0.24],
237
+ colorNoise: 0.05,
238
+ topColor: [0.8, 0.73, 0.62],
239
+ topHeightStart: 0.3,
240
+ topSlopeStart: 0.6,
241
+ },
242
+ });
243
+
244
+ // Eroded landforms ('heightfield' shape): stylized drainage gullies + thermal
245
+ // talus — the terrain-tool look at rock-piece scale.
246
+ // Displacement stages stay quiet; the erosion IS the detail.
247
+ registerRockgenPreset('eroded-mesa', {
248
+ label: 'Eroded Mesa',
249
+ meshing: { exportResolution: 288, normalsMode: 'flat', previewResolution: 96 },
250
+ piece: {
251
+ falloff: { bottomFlatten: 0 },
252
+ heightfield: {
253
+ droplets: 0.55, erosion: 0.75, profile: 'mesa', relief: 0.55,
254
+ roughness: 0.45, terrace: 0.5, terraceSteps: 7, thermal: 0.65,
255
+ },
256
+ name: 'Eroded Mesa',
257
+ noise: { amplitude: 0.02, frequency: 2.6, octaves: 2 },
258
+ shape: {
259
+ sizeX: 2.6, sizeY: 1.3, sizeZ: 2.2, type: 'heightfield',
260
+ },
261
+ warp: { enabled: false },
262
+ },
263
+ surface: {
264
+ baseColor: [0.72, 0.5, 0.34],
265
+ cavityColor: [0.44, 0.26, 0.18],
266
+ colorNoise: 0.05,
267
+ topColor: [0.84, 0.7, 0.5],
268
+ topHeightStart: 0.55,
269
+ topSlopeStart: 0.6,
270
+ },
271
+ });
272
+
273
+ registerRockgenPreset('canyon-ridge', {
274
+ label: 'Canyon Ridge',
275
+ meshing: { exportResolution: 288, normalsMode: 'flat', previewResolution: 96 },
276
+ piece: {
277
+ falloff: { bottomFlatten: 0 },
278
+ heightfield: {
279
+ // Relief past ~0.6 with 9 terrace steps out-runs what the droplets
280
+ // can smooth at patch resolution — the ridge shatters into hoodoo
281
+ // needles. Keep the backbone moderate and let erosion carve it.
282
+ droplets: 0.7, erosion: 0.85, profile: 'ridge', relief: 0.52,
283
+ roughness: 0.38, terrace: 0.55, terraceSteps: 7, thermal: 0.7,
284
+ },
285
+ name: 'Canyon Ridge',
286
+ noise: { amplitude: 0.02, frequency: 2.4, octaves: 2 },
287
+ shape: {
288
+ sizeX: 1.6, sizeY: 1.6, sizeZ: 2.8, type: 'heightfield',
289
+ },
290
+ warp: { enabled: false },
291
+ },
292
+ surface: {
293
+ baseColor: [0.68, 0.44, 0.3],
294
+ cavityColor: [0.4, 0.22, 0.16],
295
+ colorNoise: 0.06,
296
+ topColor: [0.82, 0.66, 0.46],
297
+ topHeightStart: 0.5,
298
+ topSlopeStart: 0.55,
299
+ },
300
+ });
301
+
302
+ // --- Document presets (multi-piece compositions) ---------------------------
303
+
304
+ const ARCH_COLUMN_PIECE = {
305
+ columns: {
306
+ enabled: true, grooveDepth: 0.17, grooveWidth: 0.13, heightVariation: 1.2, scale: 1.7,
307
+ },
308
+ falloff: { bottomFlatten: 0.35 },
309
+ noise: { amplitude: 0.035, frequency: 2.4, octaves: 3 },
310
+ strata: {
311
+ enabled: true, frequency: 2.0, sharpness: 0.9, strength: 0.04, tiltDegrees: 0, warpAmount: 0.05,
312
+ },
313
+ warp: { frequency: 0.4, strength: 0.08 },
314
+ };
315
+
316
+ // A natural stone arch (Liyue-style): one wide columnar wall with an
317
+ // ellipsoid subtracted through it for the opening, flanked by a second
318
+ // bundle for an asymmetric skyline.
319
+ registerRockgenPreset('column-arch', {
320
+ kind: 'document',
321
+ label: 'Column Arch',
322
+ meshing: { exportResolution: 288, normalsMode: 'flat', previewResolution: 96 },
323
+ pieces: [
324
+ {
325
+ ...ARCH_COLUMN_PIECE,
326
+ name: 'Arch Wall',
327
+ shape: {
328
+ cornerRadius: 0.2, sizeX: 3.0, sizeY: 2.6, sizeZ: 1.0, type: 'box',
329
+ },
330
+ },
331
+ {
332
+ ...ARCH_COLUMN_PIECE,
333
+ combine: { blend: 0.5, op: 'smoothUnion' },
334
+ name: 'Flank Bundle',
335
+ seed: 3,
336
+ shape: {
337
+ cornerRadius: 0.25, sizeX: 1.1, sizeY: 1.9, sizeZ: 1.2, type: 'box',
338
+ },
339
+ transform: { position: [-2.9, -0.7, 0.4] },
340
+ },
341
+ {
342
+ combine: { blend: 0.18, op: 'subtract' },
343
+ name: 'Opening',
344
+ noise: { amplitude: 0.12, frequency: 1.6, octaves: 3 },
345
+ shape: { sizeX: 1.6, sizeY: 2.05, sizeZ: 3.2, type: 'ellipsoid' },
346
+ transform: { position: [0.45, -1.45, 0] },
347
+ warp: { frequency: 0.6, strength: 0.2 },
348
+ },
349
+ ],
350
+ surface: {
351
+ baseColor: [0.68, 0.58, 0.48],
352
+ cavityColor: [0.4, 0.31, 0.24],
353
+ colorNoise: 0.05,
354
+ topColor: [0.8, 0.73, 0.62],
355
+ topHeightStart: 0.3,
356
+ topSlopeStart: 0.6,
357
+ },
358
+ });
359
+
360
+ // Shared slab recipe for the cliff-face composition below.
361
+ const CLIFF_SLAB_PIECE = {
362
+ cuts: {
363
+ bevel: 0.02, count: 8, depth: 0.3, enabled: true, verticalBias: 0.8,
364
+ },
365
+ noise: { amplitude: 0.03, frequency: 2.2, octaves: 3 },
366
+ strata: {
367
+ enabled: true, frequency: 1.8, sharpness: 0.85, strength: 0.04, tiltDegrees: 2, warpAmount: 0.08,
368
+ },
369
+ warp: { enabled: false, frequency: 0.4, strength: 0.08 },
370
+ };
371
+
372
+ // A stacked cliff face (anime-style): staggered flat-cut slabs joined
373
+ // with HARD unions — smoothUnion would round the seams away; the crisp
374
+ // slab-on-slab joints are the look.
375
+ registerRockgenPreset('cliff-face', {
376
+ kind: 'document',
377
+ label: 'Cliff Face',
378
+ meshing: { exportResolution: 288, normalsMode: 'flat', previewResolution: 96 },
379
+ pieces: [
380
+ {
381
+ ...CLIFF_SLAB_PIECE,
382
+ falloff: { bottomFlatten: 0.5 },
383
+ name: 'Lower Wall',
384
+ shape: {
385
+ cornerRadius: 0.02, sizeX: 2.9, sizeY: 1.9, sizeZ: 1.15, type: 'box',
386
+ },
387
+ },
388
+ {
389
+ ...CLIFF_SLAB_PIECE,
390
+ combine: { blend: 0, op: 'union' },
391
+ name: 'Mid Wall',
392
+ seed: 4,
393
+ shape: {
394
+ cornerRadius: 0.02, sizeX: 2.4, sizeY: 1.5, sizeZ: 0.95, type: 'box',
395
+ },
396
+ transform: { position: [0.35, 2.6, -0.3], rotation: [0, 0.12, 0] },
397
+ },
398
+ {
399
+ ...CLIFF_SLAB_PIECE,
400
+ combine: { blend: 0, op: 'union' },
401
+ name: 'Crown Slab',
402
+ seed: 9,
403
+ shape: {
404
+ cornerRadius: 0.03, sizeX: 1.7, sizeY: 1.0, sizeZ: 0.8, type: 'box',
405
+ },
406
+ transform: { position: [-0.5, 4.4, -0.55], rotation: [0, -0.2, 0] },
407
+ },
408
+ {
409
+ ...CLIFF_SLAB_PIECE,
410
+ combine: { blend: 0, op: 'union' },
411
+ falloff: { bottomFlatten: 0.4 },
412
+ name: 'Shoulder Block',
413
+ seed: 13,
414
+ shape: {
415
+ cornerRadius: 0.03, sizeX: 1.1, sizeY: 1.1, sizeZ: 0.9, type: 'box',
416
+ },
417
+ transform: { position: [2.9, -0.8, 0.35], rotation: [0, 0.35, 0] },
418
+ },
419
+ ],
420
+ surface: {
421
+ baseColor: [0.68, 0.58, 0.48],
422
+ cavityColor: [0.4, 0.31, 0.24],
423
+ colorNoise: 0.05,
424
+ topColor: [0.8, 0.73, 0.62],
425
+ topHeightStart: 0.3,
426
+ topSlopeStart: 0.6,
427
+ },
428
+ });
429
+
430
+ // A boulder with satellite rubble — demonstrates hard-union clustering.
431
+ const SCREE_ROCK_PIECE = {
432
+ cuts: {
433
+ bevel: 0.025, count: 6, depth: 0.25, enabled: true, verticalBias: 0.3,
434
+ },
435
+ falloff: { bottomFlatten: 0.35 },
436
+ noise: { amplitude: 0.06, frequency: 1.4, octaves: 3 },
437
+ warp: { frequency: 0.7, strength: 0.06 },
438
+ };
439
+
440
+ registerRockgenPreset('scree-cluster', {
441
+ kind: 'document',
442
+ label: 'Scree Cluster',
443
+ meshing: { normalsMode: 'flat' },
444
+ pieces: [
445
+ {
446
+ ...SCREE_ROCK_PIECE,
447
+ falloff: { bottomFlatten: 0.4 },
448
+ name: 'Main Boulder',
449
+ shape: { sizeX: 1.15, sizeY: 0.8, sizeZ: 1.0 },
450
+ },
451
+ {
452
+ ...SCREE_ROCK_PIECE,
453
+ combine: { blend: 0, op: 'union' },
454
+ name: 'Rubble A',
455
+ seed: 5,
456
+ shape: { sizeX: 0.5, sizeY: 0.35, sizeZ: 0.45 },
457
+ transform: { position: [1.3, -0.5, 0.4], rotation: [0, 0.7, 0] },
458
+ },
459
+ {
460
+ ...SCREE_ROCK_PIECE,
461
+ combine: { blend: 0, op: 'union' },
462
+ name: 'Rubble B',
463
+ seed: 9,
464
+ shape: { sizeX: 0.4, sizeY: 0.3, sizeZ: 0.5 },
465
+ transform: { position: [-1.15, -0.55, -0.5], rotation: [0, -0.4, 0] },
466
+ },
467
+ ],
468
+ surface: {
469
+ baseColor: [0.56, 0.53, 0.48],
470
+ cavityColor: [0.36, 0.33, 0.29],
471
+ topColor: [0.75, 0.76, 0.72],
472
+ topHeightStart: 0.5,
473
+ topSlopeStart: 0.6,
474
+ },
475
+ });
476
+
477
+ // The classic low-poly look: the COARSE GRID is the art style. Preview and
478
+ // export mesh at the same resolution so exports are exactly what you see;
479
+ // flat normals turn every surface-nets quad into a readable facet.
480
+ registerRockgenPreset('lowpoly-boulder', {
481
+ label: 'Low-Poly Boulder',
482
+ meshing: { exportResolution: 32, normalsMode: 'flat', previewResolution: 32 },
483
+ piece: {
484
+ falloff: { bottomFlatten: 0.4 },
485
+ name: 'Low-Poly Boulder',
486
+ noise: { amplitude: 0.16, frequency: 1.0, octaves: 3 },
487
+ shape: { sizeX: 1.2, sizeY: 0.85, sizeZ: 1.0 },
488
+ warp: { frequency: 0.7, strength: 0.25 },
489
+ },
490
+ surface: {
491
+ baseColor: [0.56, 0.53, 0.48],
492
+ cavityColor: [0.36, 0.33, 0.29],
493
+ colorNoise: 0.03,
494
+ topColor: [0.75, 0.76, 0.72],
495
+ topHeightStart: 0.5,
496
+ topSlopeStart: 0.6,
497
+ },
498
+ });
499
+
500
+ registerRockgenPreset('mossy-boulder', {
501
+ label: 'Mossy Boulder',
502
+ meshing: { normalsMode: 'flat' },
503
+ piece: {
504
+ cuts: {
505
+ bevel: 0.04, count: 5, depth: 0.2, enabled: true, verticalBias: 0.3,
506
+ },
507
+ falloff: { bottomFlatten: 0.42 },
508
+ name: 'Mossy Boulder',
509
+ noise: { amplitude: 0.06, frequency: 1.2, octaves: 3 },
510
+ shape: { sizeX: 1.1, sizeY: 0.8, sizeZ: 0.95 },
511
+ warp: { frequency: 0.7, strength: 0.08 },
512
+ },
513
+ surface: {
514
+ baseColor: [0.5, 0.47, 0.42],
515
+ cavityColor: [0.28, 0.26, 0.22],
516
+ colorNoise: 0.05,
517
+ lichenCoverage: 0.16,
518
+ mossColor: [0.24, 0.39, 0.18],
519
+ mossCoverage: 0.62,
520
+ textureScale: 1.2,
521
+ textureStrength: 0.18,
522
+ textureStyle: 'granite',
523
+ topColor: [0.55, 0.61, 0.43],
524
+ topHeightStart: 0.4,
525
+ topSlopeStart: 0.5,
526
+ },
527
+ });
528
+
529
+ // ('desert-mesa' retired: the eroded-mesa heightfield preset supersedes the
530
+ // old box-with-strata approximation. normalizeRockgenPresetName falls saved
531
+ // documents back to 'boulder' gracefully.)
532
+
533
+ registerRockgenPreset('shard-monolith', {
534
+ label: 'Shard Monolith (Flat)',
535
+ meshing: { normalsMode: 'flat' },
536
+ piece: {
537
+ // The shard look comes from planar cuts, not facet grooves — cuts leave
538
+ // dead-flat faces and sharp edges; heavy facet strength just shatters
539
+ // the box into rounded blobs.
540
+ cuts: {
541
+ bevel: 0.015, count: 7, depth: 0.4, enabled: true, verticalBias: 0.45,
542
+ },
543
+ falloff: { bottomFlatten: 0.4 },
544
+ name: 'Shard Monolith',
545
+ noise: { amplitude: 0.025, frequency: 1.8, octaves: 3 },
546
+ shape: {
547
+ cornerRadius: 0.04, sizeX: 0.7, sizeY: 2.0, sizeZ: 0.55, type: 'box',
548
+ },
549
+ warp: { frequency: 0.45, strength: 0.05 },
550
+ },
551
+ surface: {
552
+ baseColor: [0.5, 0.54, 0.6],
553
+ cavityColor: [0.26, 0.28, 0.34],
554
+ colorNoise: 0.03,
555
+ topColor: [0.72, 0.77, 0.82],
556
+ topHeightStart: 0.55,
557
+ topSlopeStart: 0.55,
558
+ },
559
+ });