@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.
- package/ATTRIBUTION.md +40 -0
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/package.json +97 -0
- package/src/character/characterRig.js +353 -0
- package/src/character/freestyleSwimClip.js +541 -0
- package/src/character/index.js +4 -0
- package/src/character/modelLoader.js +433 -0
- package/src/core/materialRoles.js +438 -0
- package/src/core/presetDocuments.js +110 -0
- package/src/core/shaderBackend.js +24 -0
- package/src/debrisgen/debrisFields.js +544 -0
- package/src/debrisgen/debrisGenerator.js +1929 -0
- package/src/debrisgen/debrisPalettes.js +71 -0
- package/src/debrisgen/debrisPhysics.js +198 -0
- package/src/debrisgen/debrisPresets.js +155 -0
- package/src/debrisgen/debrisSettings.js +333 -0
- package/src/debrisgen/debrisTextures.js +380 -0
- package/src/debrisgen/index.js +5 -0
- package/src/debug/fieldValues.js +95 -0
- package/src/debug/index.js +7 -0
- package/src/debug/settingsPanel.js +170 -0
- package/src/environment/environmentAmbientProbe.js +160 -0
- package/src/environment/environmentMaterialAdapter.js +336 -0
- package/src/environment/environmentMaterialClassifier.js +178 -0
- package/src/environment/environmentPlanarReflection.js +201 -0
- package/src/environment/environmentPresets.js +443 -0
- package/src/environment/environmentRigs.js +684 -0
- package/src/environment/environmentSettings.js +504 -0
- package/src/environment/environmentShaderMaterials.js +318 -0
- package/src/environment/environmentSunShadowPass.js +268 -0
- package/src/environment/environmentTextureResolver.js +161 -0
- package/src/environment/environmentTimeOfDay.js +167 -0
- package/src/environment/environmentVertexAo.js +188 -0
- package/src/environment/index.js +8 -0
- package/src/environment/scanAssetStylize.js +103 -0
- package/src/index.js +21 -0
- package/src/loaders/index.js +1 -0
- package/src/post/index.js +2 -0
- package/src/post/postProcessing.js +1087 -0
- package/src/rockgen/export/glbExport.js +177 -0
- package/src/rockgen/heightfield/heightfieldErosion.js +7 -0
- package/src/rockgen/heightfield/heightfieldPatch.js +183 -0
- package/src/rockgen/heightfield/stylizedErosionSim.js +356 -0
- package/src/rockgen/index.js +14 -0
- package/src/rockgen/mesh/meshAttributes.js +374 -0
- package/src/rockgen/mesh/meshDocument.js +158 -0
- package/src/rockgen/mesh/surfaceNets.js +329 -0
- package/src/rockgen/noise/cellularNoise3.js +92 -0
- package/src/rockgen/noise/prng.js +62 -0
- package/src/rockgen/noise/simplexNoise3.js +89 -0
- package/src/rockgen/noise/valueNoise3.js +87 -0
- package/src/rockgen/rockDocument.js +255 -0
- package/src/rockgen/rockHelpers.js +8 -0
- package/src/rockgen/rockgenPresets.js +559 -0
- package/src/rockgen/rockgenSettings.js +756 -0
- package/src/rockgen/sdf/fieldCompiler.js +544 -0
- package/src/rockgen/sdf/sculptEdits.js +66 -0
- package/src/rockgen/sdf/sdfModifiers.js +45 -0
- package/src/rockgen/sdf/sdfOps.js +37 -0
- package/src/rockgen/sdf/sdfPrimitives.js +88 -0
- package/src/shaders-tsl/anime.js +963 -0
- package/src/shaders-tsl/chunks/character-color.js +64 -0
- package/src/shaders-tsl/chunks/character-highlights.js +174 -0
- package/src/shaders-tsl/chunks/character-lighting.js +309 -0
- package/src/shaders-tsl/chunks/character-material-maps.js +146 -0
- package/src/shaders-tsl/chunks/character-roles.js +52 -0
- package/src/shaders-tsl/chunks/character-scene-lights.js +270 -0
- package/src/shaders-tsl/chunks/character-shadow-color.js +61 -0
- package/src/shaders-tsl/chunks/character-skinning.js +140 -0
- package/src/shaders-tsl/chunks/environment-color.js +47 -0
- package/src/shaders-tsl/chunks/environment-debug.js +79 -0
- package/src/shaders-tsl/chunks/environment-lighting.js +260 -0
- package/src/shaders-tsl/chunks/environment-sun-shadow.js +87 -0
- package/src/shaders-tsl/chunks/foliage-fog.js +60 -0
- package/src/shaders-tsl/chunks/pass-depth-color.js +89 -0
- package/src/shaders-tsl/chunks/stylized-cloud-shadow.js +70 -0
- package/src/shaders-tsl/chunks/water-color.js +190 -0
- package/src/shaders-tsl/chunks/water-common.js +124 -0
- package/src/shaders-tsl/chunks/water-foam.js +93 -0
- package/src/shaders-tsl/chunks/water-lighting.js +116 -0
- package/src/shaders-tsl/chunks/water-ripple.js +64 -0
- package/src/shaders-tsl/chunks/water-waves.js +100 -0
- package/src/shaders-tsl/environment-ao-overlay.js +60 -0
- package/src/shaders-tsl/environment.js +680 -0
- package/src/shaders-tsl/flower.js +267 -0
- package/src/shaders-tsl/grass.js +193 -0
- package/src/shaders-tsl/post-composite.js +461 -0
- package/src/shaders-tsl/sky.js +175 -0
- package/src/shaders-tsl/tree-leaf.js +264 -0
- package/src/shaders-tsl/water-breaker.js +423 -0
- package/src/shaders-tsl/water-kelp.js +171 -0
- package/src/shaders-tsl/water-rain.js +107 -0
- package/src/shaders-tsl/water-simulation.js +140 -0
- package/src/shaders-tsl/water-splash.js +298 -0
- package/src/shaders-tsl/water.js +509 -0
- package/src/sky/index.js +2 -0
- package/src/sky/stylizedSky.js +341 -0
- package/src/toon/characterRenderPasses.js +646 -0
- package/src/toon/index.js +4 -0
- package/src/toon/settings/alphaSettings.js +170 -0
- package/src/toon/settings/averageShadowSettings.js +181 -0
- package/src/toon/settings/baseTextureSettings.js +133 -0
- package/src/toon/settings/celShadeSettings.js +96 -0
- package/src/toon/settings/contactShadowSettings.js +114 -0
- package/src/toon/settings/eyeHighlightSettings.js +187 -0
- package/src/toon/settings/faceLightingSettings.js +135 -0
- package/src/toon/settings/furSettings.js +112 -0
- package/src/toon/settings/glitterSettings.js +102 -0
- package/src/toon/settings/hairHighlightSettings.js +325 -0
- package/src/toon/settings/indirectLightSettings.js +244 -0
- package/src/toon/settings/localLightSettings.js +176 -0
- package/src/toon/settings/materialMapSettings.js +299 -0
- package/src/toon/settings/outlineSettings.js +263 -0
- package/src/toon/settings/perspectiveRemovalSettings.js +60 -0
- package/src/toon/settings/rimLightSettings.js +268 -0
- package/src/toon/settings/sceneShadowSettings.js +147 -0
- package/src/toon/settings/selfShadowSettings.js +221 -0
- package/src/toon/settings/shadowColorSettings.js +238 -0
- package/src/toon/settings/skinToneSettings.js +150 -0
- package/src/toon/settings/specularSettings.js +314 -0
- package/src/toon/settings/stickerSettings.js +102 -0
- package/src/toon/toonMaterialAdapter.js +2235 -0
- package/src/toon/toonSettings.js +860 -0
- package/src/vegetation/flowerSpecies.js +260 -0
- package/src/vegetation/index.js +10 -0
- package/src/vegetation/stylizedBush.js +136 -0
- package/src/vegetation/stylizedFlower.js +213 -0
- package/src/vegetation/stylizedFlowers.js +297 -0
- package/src/vegetation/stylizedGrass.js +491 -0
- package/src/vegetation/stylizedTree.js +2449 -0
- package/src/vegetation/stylizedTreeFoliage.js +691 -0
- package/src/vegetation/treeExport.js +300 -0
- package/src/vegetation/treeRecipe.js +647 -0
- package/src/water/index.js +2 -0
- package/src/water/water.js +20 -0
- package/src/water/waterBreakerSystem.js +591 -0
- package/src/water/waterInteraction.js +193 -0
- package/src/water/waterMaterial.js +263 -0
- package/src/water/waterRain.js +136 -0
- package/src/water/waterRippleSimulation.js +227 -0
- package/src/water/waterScenePasses.js +415 -0
- package/src/water/waterSettings.js +1138 -0
- package/src/water/waterSplashSystem.js +251 -0
- package/src/water/waterSurface.js +475 -0
- package/src/water/waterVegetation.js +83 -0
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
// Rockgen settings schema: frozen defaults per setting group, group
|
|
2
|
+
// metadata, and the auto-generated field schema the debug settings panel
|
|
3
|
+
// renders (same pattern as environmentSettings.js). Piece-level groups
|
|
4
|
+
// (shape/heightfield/noise/warp/cuts/facet/cracks/strata/columns/falloff)
|
|
5
|
+
// apply to one rock piece;
|
|
6
|
+
// document-level groups (surface/meshing) apply to the whole document.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base primitive the piece displaces. 'sketch' extrudes the piece's drawn
|
|
10
|
+
* `outline` polygon (piece-level data, not a schema field) and falls back
|
|
11
|
+
* to the ellipsoid until an outline exists.
|
|
12
|
+
*/
|
|
13
|
+
export const ROCK_SHAPE_TYPES = Object.freeze(['ellipsoid', 'sphere', 'box', 'capsule', 'sketch', 'heightfield']);
|
|
14
|
+
|
|
15
|
+
/** Normal generation modes for meshed documents. */
|
|
16
|
+
export const ROCK_NORMALS_MODES = Object.freeze(['gradient', 'flat']);
|
|
17
|
+
|
|
18
|
+
/** Procedural albedo styles layered into baked vertex colors. */
|
|
19
|
+
export const ROCK_SURFACE_TEXTURE_STYLES = Object.freeze([
|
|
20
|
+
'none',
|
|
21
|
+
'granite',
|
|
22
|
+
'sandstone',
|
|
23
|
+
'basalt',
|
|
24
|
+
'limestone',
|
|
25
|
+
'veined',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_ROCK_SHAPE_SETTINGS = Object.freeze({
|
|
29
|
+
capsuleLength: 1.5,
|
|
30
|
+
cornerRadius: 0.25,
|
|
31
|
+
sizeX: 1.0,
|
|
32
|
+
sizeY: 0.75,
|
|
33
|
+
sizeZ: 1.0,
|
|
34
|
+
type: 'ellipsoid',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Default displacement is deliberately quiet: noise past ~0.12 amplitude or
|
|
38
|
+
// warp past ~0.15 strength turns a fresh piece into a melted blob before
|
|
39
|
+
// the user touches anything.
|
|
40
|
+
export const DEFAULT_ROCK_NOISE_SETTINGS = Object.freeze({
|
|
41
|
+
amplitude: 0.08,
|
|
42
|
+
enabled: true,
|
|
43
|
+
frequency: 1.4,
|
|
44
|
+
gain: 0.5,
|
|
45
|
+
lacunarity: 2.0,
|
|
46
|
+
octaves: 3,
|
|
47
|
+
ridged: false,
|
|
48
|
+
seedOffset: 0,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export const DEFAULT_ROCK_WARP_SETTINGS = Object.freeze({
|
|
52
|
+
enabled: true,
|
|
53
|
+
frequency: 0.9,
|
|
54
|
+
strength: 0.1,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/** Silhouette profiles for the eroded heightfield shape. */
|
|
58
|
+
export const ROCK_HEIGHTFIELD_PROFILES = Object.freeze(['mesa', 'ridge', 'slope', 'open']);
|
|
59
|
+
|
|
60
|
+
// Eroded heightfield ('heightfield' shape type): ridged relief shaped by a
|
|
61
|
+
// silhouette profile, terraced into strata bands, then run through ToonLab's
|
|
62
|
+
// first-party stylized drainage + thermal settling model.
|
|
63
|
+
export const DEFAULT_ROCK_HEIGHTFIELD_SETTINGS = Object.freeze({
|
|
64
|
+
droplets: 0.5,
|
|
65
|
+
erosion: 0.7,
|
|
66
|
+
profile: 'mesa',
|
|
67
|
+
relief: 0.65,
|
|
68
|
+
roughness: 0.5,
|
|
69
|
+
seedOffset: 0,
|
|
70
|
+
terrace: 0.35,
|
|
71
|
+
terraceSteps: 6,
|
|
72
|
+
thermal: 0.6,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Planar cuts: seeded flat half-space slices hard-intersected with the
|
|
76
|
+
// shape. The one stage whose output is genuinely planar — cut faces are
|
|
77
|
+
// applied in unwarped local space, after noise, so warp and fbm never
|
|
78
|
+
// bend them. This is what makes slabs, shards, and cliff walls possible;
|
|
79
|
+
// every other stage is a smooth displacement and can only round.
|
|
80
|
+
export const DEFAULT_ROCK_CUTS_SETTINGS = Object.freeze({
|
|
81
|
+
bevel: 0.02,
|
|
82
|
+
count: 6,
|
|
83
|
+
depth: 0.3,
|
|
84
|
+
enabled: false,
|
|
85
|
+
seedOffset: 0,
|
|
86
|
+
verticalBias: 0.6,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const DEFAULT_ROCK_FACET_SETTINGS = Object.freeze({
|
|
90
|
+
enabled: false,
|
|
91
|
+
jitter: 1.0,
|
|
92
|
+
scale: 2.2,
|
|
93
|
+
strength: 0.25,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Cracks: SPARSE deep fissures along large Voronoi cell borders, gated by
|
|
97
|
+
// a low-frequency coverage mask so they read as weathering patches — the
|
|
98
|
+
// Blender-geometry-nodes rock recipe's crack layer, distinct from facet
|
|
99
|
+
// (dense soft grooves everywhere).
|
|
100
|
+
export const DEFAULT_ROCK_CRACKS_SETTINGS = Object.freeze({
|
|
101
|
+
coverage: 0.55,
|
|
102
|
+
depth: 0.08,
|
|
103
|
+
enabled: false,
|
|
104
|
+
scale: 0.9,
|
|
105
|
+
width: 0.09,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export const DEFAULT_ROCK_STRATA_SETTINGS = Object.freeze({
|
|
109
|
+
enabled: false,
|
|
110
|
+
frequency: 3,
|
|
111
|
+
sharpness: 0.6,
|
|
112
|
+
strength: 0.12,
|
|
113
|
+
tiltDegrees: 8,
|
|
114
|
+
warpAmount: 0.2,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Columnar jointing (basalt columns / Liyue stone forest): 2D Voronoi cells
|
|
118
|
+
// in the ground plane become vertical prismatic columns — grooves at cell
|
|
119
|
+
// borders, a per-column height offset for the stepped skyline, and (when
|
|
120
|
+
// strata is on) per-column joint phase so break lines don't align.
|
|
121
|
+
export const DEFAULT_ROCK_COLUMNS_SETTINGS = Object.freeze({
|
|
122
|
+
enabled: false,
|
|
123
|
+
grooveDepth: 0.14,
|
|
124
|
+
grooveWidth: 0.25,
|
|
125
|
+
heightVariation: 0.9,
|
|
126
|
+
scale: 1.6,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
export const DEFAULT_ROCK_FALLOFF_SETTINGS = Object.freeze({
|
|
130
|
+
bottomFlatten: 0,
|
|
131
|
+
radialPinch: 0,
|
|
132
|
+
topTaper: 0,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export const DEFAULT_ROCK_SURFACE_SETTINGS = Object.freeze({
|
|
136
|
+
baseColor: Object.freeze([0.54, 0.52, 0.47]),
|
|
137
|
+
cavityColor: Object.freeze([0.34, 0.31, 0.25]),
|
|
138
|
+
topColor: Object.freeze([0.71, 0.73, 0.68]),
|
|
139
|
+
topCoatStrength: 1,
|
|
140
|
+
topHeightStart: 0.55,
|
|
141
|
+
topSlopeStart: 0.72,
|
|
142
|
+
colorNoise: 0.06,
|
|
143
|
+
textureStyle: 'none',
|
|
144
|
+
textureStrength: 0,
|
|
145
|
+
textureScale: 1,
|
|
146
|
+
veinColor: Object.freeze([0.82, 0.8, 0.72]),
|
|
147
|
+
veinStrength: 0,
|
|
148
|
+
stainColor: Object.freeze([0.56, 0.38, 0.2]),
|
|
149
|
+
stainStrength: 0,
|
|
150
|
+
mossColor: Object.freeze([0.28, 0.42, 0.2]),
|
|
151
|
+
mossCoverage: 0,
|
|
152
|
+
lichenColor: Object.freeze([0.68, 0.72, 0.5]),
|
|
153
|
+
lichenCoverage: 0,
|
|
154
|
+
aoRadius: 0.5,
|
|
155
|
+
aoStrength: 1,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
export const ROCK_SURFACE_TEXTURE_PRESETS = Object.freeze({
|
|
159
|
+
bare: Object.freeze({
|
|
160
|
+
description: 'Plain baked rock colors with only the height/cavity coat.',
|
|
161
|
+
label: 'Bare',
|
|
162
|
+
surface: Object.freeze({
|
|
163
|
+
baseColor: Object.freeze([0.54, 0.52, 0.47]),
|
|
164
|
+
cavityColor: Object.freeze([0.34, 0.31, 0.25]),
|
|
165
|
+
colorNoise: 0.06,
|
|
166
|
+
lichenCoverage: 0,
|
|
167
|
+
mossCoverage: 0,
|
|
168
|
+
stainStrength: 0,
|
|
169
|
+
textureScale: 1,
|
|
170
|
+
textureStrength: 0,
|
|
171
|
+
textureStyle: 'none',
|
|
172
|
+
topCoatStrength: 1,
|
|
173
|
+
topColor: Object.freeze([0.71, 0.73, 0.68]),
|
|
174
|
+
topHeightStart: 0.55,
|
|
175
|
+
topSlopeStart: 0.72,
|
|
176
|
+
veinStrength: 0,
|
|
177
|
+
}),
|
|
178
|
+
}),
|
|
179
|
+
granite: Object.freeze({
|
|
180
|
+
description: 'Cool speckled stone with restrained pale flecks.',
|
|
181
|
+
label: 'Granite',
|
|
182
|
+
surface: Object.freeze({
|
|
183
|
+
baseColor: Object.freeze([0.56, 0.55, 0.53]),
|
|
184
|
+
cavityColor: Object.freeze([0.31, 0.3, 0.31]),
|
|
185
|
+
colorNoise: 0.04,
|
|
186
|
+
lichenCoverage: 0.04,
|
|
187
|
+
mossCoverage: 0,
|
|
188
|
+
stainStrength: 0.05,
|
|
189
|
+
textureScale: 1.5,
|
|
190
|
+
textureStrength: 0.32,
|
|
191
|
+
textureStyle: 'granite',
|
|
192
|
+
topColor: Object.freeze([0.78, 0.79, 0.76]),
|
|
193
|
+
veinStrength: 0.08,
|
|
194
|
+
}),
|
|
195
|
+
}),
|
|
196
|
+
sandstone: Object.freeze({
|
|
197
|
+
description: 'Warm sediment bands, dusted ledges, and light iron wash.',
|
|
198
|
+
label: 'Sandstone',
|
|
199
|
+
surface: Object.freeze({
|
|
200
|
+
baseColor: Object.freeze([0.67, 0.47, 0.32]),
|
|
201
|
+
cavityColor: Object.freeze([0.38, 0.24, 0.17]),
|
|
202
|
+
colorNoise: 0.05,
|
|
203
|
+
lichenCoverage: 0,
|
|
204
|
+
mossCoverage: 0,
|
|
205
|
+
stainColor: Object.freeze([0.62, 0.32, 0.16]),
|
|
206
|
+
stainStrength: 0.22,
|
|
207
|
+
textureScale: 1.35,
|
|
208
|
+
textureStrength: 0.38,
|
|
209
|
+
textureStyle: 'sandstone',
|
|
210
|
+
topColor: Object.freeze([0.82, 0.66, 0.45]),
|
|
211
|
+
veinStrength: 0,
|
|
212
|
+
}),
|
|
213
|
+
}),
|
|
214
|
+
basalt: Object.freeze({
|
|
215
|
+
description: 'Dark volcanic stone with blocky crystal breakup.',
|
|
216
|
+
label: 'Basalt',
|
|
217
|
+
surface: Object.freeze({
|
|
218
|
+
baseColor: Object.freeze([0.34, 0.35, 0.36]),
|
|
219
|
+
cavityColor: Object.freeze([0.18, 0.18, 0.2]),
|
|
220
|
+
colorNoise: 0.035,
|
|
221
|
+
lichenCoverage: 0.06,
|
|
222
|
+
mossCoverage: 0,
|
|
223
|
+
stainStrength: 0,
|
|
224
|
+
textureScale: 1.1,
|
|
225
|
+
textureStrength: 0.34,
|
|
226
|
+
textureStyle: 'basalt',
|
|
227
|
+
topColor: Object.freeze([0.52, 0.54, 0.55]),
|
|
228
|
+
veinStrength: 0.03,
|
|
229
|
+
}),
|
|
230
|
+
}),
|
|
231
|
+
limestone: Object.freeze({
|
|
232
|
+
description: 'Pale chalky patches with soft mineral clouding.',
|
|
233
|
+
label: 'Limestone',
|
|
234
|
+
surface: Object.freeze({
|
|
235
|
+
baseColor: Object.freeze([0.63, 0.6, 0.52]),
|
|
236
|
+
cavityColor: Object.freeze([0.38, 0.34, 0.28]),
|
|
237
|
+
colorNoise: 0.045,
|
|
238
|
+
lichenCoverage: 0.14,
|
|
239
|
+
mossCoverage: 0,
|
|
240
|
+
stainStrength: 0.08,
|
|
241
|
+
textureScale: 1.25,
|
|
242
|
+
textureStrength: 0.28,
|
|
243
|
+
textureStyle: 'limestone',
|
|
244
|
+
topColor: Object.freeze([0.78, 0.77, 0.68]),
|
|
245
|
+
veinStrength: 0.04,
|
|
246
|
+
}),
|
|
247
|
+
}),
|
|
248
|
+
veined: Object.freeze({
|
|
249
|
+
description: 'Quartz/mineral seams over a cool rock base.',
|
|
250
|
+
label: 'Veined',
|
|
251
|
+
surface: Object.freeze({
|
|
252
|
+
baseColor: Object.freeze([0.48, 0.5, 0.53]),
|
|
253
|
+
cavityColor: Object.freeze([0.25, 0.27, 0.31]),
|
|
254
|
+
colorNoise: 0.035,
|
|
255
|
+
lichenCoverage: 0.02,
|
|
256
|
+
mossCoverage: 0,
|
|
257
|
+
textureScale: 1.05,
|
|
258
|
+
textureStrength: 0.18,
|
|
259
|
+
textureStyle: 'veined',
|
|
260
|
+
topColor: Object.freeze([0.68, 0.72, 0.74]),
|
|
261
|
+
veinColor: Object.freeze([0.88, 0.86, 0.78]),
|
|
262
|
+
veinStrength: 0.68,
|
|
263
|
+
}),
|
|
264
|
+
}),
|
|
265
|
+
mossy: Object.freeze({
|
|
266
|
+
description: 'Green ledge/cavity growth plus small pale lichen spots.',
|
|
267
|
+
label: 'Moss',
|
|
268
|
+
surface: Object.freeze({
|
|
269
|
+
baseColor: Object.freeze([0.48, 0.46, 0.41]),
|
|
270
|
+
cavityColor: Object.freeze([0.27, 0.25, 0.21]),
|
|
271
|
+
colorNoise: 0.05,
|
|
272
|
+
lichenCoverage: 0.16,
|
|
273
|
+
mossColor: Object.freeze([0.24, 0.39, 0.18]),
|
|
274
|
+
mossCoverage: 0.62,
|
|
275
|
+
stainStrength: 0.06,
|
|
276
|
+
textureScale: 1.2,
|
|
277
|
+
textureStrength: 0.18,
|
|
278
|
+
textureStyle: 'granite',
|
|
279
|
+
topColor: Object.freeze([0.55, 0.61, 0.43]),
|
|
280
|
+
topHeightStart: 0.4,
|
|
281
|
+
topSlopeStart: 0.5,
|
|
282
|
+
veinStrength: 0.02,
|
|
283
|
+
}),
|
|
284
|
+
}),
|
|
285
|
+
lichen: Object.freeze({
|
|
286
|
+
description: 'Dry exposed-face lichen over pale rock.',
|
|
287
|
+
label: 'Lichen',
|
|
288
|
+
surface: Object.freeze({
|
|
289
|
+
baseColor: Object.freeze([0.55, 0.54, 0.48]),
|
|
290
|
+
cavityColor: Object.freeze([0.32, 0.3, 0.25]),
|
|
291
|
+
colorNoise: 0.045,
|
|
292
|
+
lichenColor: Object.freeze([0.72, 0.74, 0.52]),
|
|
293
|
+
lichenCoverage: 0.48,
|
|
294
|
+
mossCoverage: 0.08,
|
|
295
|
+
stainStrength: 0.04,
|
|
296
|
+
textureScale: 1.35,
|
|
297
|
+
textureStrength: 0.24,
|
|
298
|
+
textureStyle: 'limestone',
|
|
299
|
+
topColor: Object.freeze([0.76, 0.76, 0.65]),
|
|
300
|
+
veinStrength: 0.02,
|
|
301
|
+
}),
|
|
302
|
+
}),
|
|
303
|
+
snowcap: Object.freeze({
|
|
304
|
+
description: 'Cold rock with a broken top-facing snow/dust cap.',
|
|
305
|
+
label: 'Snow Cap',
|
|
306
|
+
surface: Object.freeze({
|
|
307
|
+
baseColor: Object.freeze([0.47, 0.49, 0.52]),
|
|
308
|
+
cavityColor: Object.freeze([0.25, 0.27, 0.31]),
|
|
309
|
+
colorNoise: 0.035,
|
|
310
|
+
lichenCoverage: 0,
|
|
311
|
+
mossCoverage: 0,
|
|
312
|
+
stainStrength: 0,
|
|
313
|
+
textureScale: 1.0,
|
|
314
|
+
textureStrength: 0.18,
|
|
315
|
+
textureStyle: 'granite',
|
|
316
|
+
topCoatStrength: 1,
|
|
317
|
+
topColor: Object.freeze([0.9, 0.93, 0.95]),
|
|
318
|
+
topHeightStart: 0.45,
|
|
319
|
+
topSlopeStart: 0.52,
|
|
320
|
+
veinStrength: 0.02,
|
|
321
|
+
}),
|
|
322
|
+
}),
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
export const DEFAULT_ROCKGEN_MESHING_SETTINGS = Object.freeze({
|
|
326
|
+
// GLB exports carry <name>_LOD0/1/2 at full/half/quarter resolution —
|
|
327
|
+
// independent SDF re-meshes, not decimations, so every level keeps
|
|
328
|
+
// clean toon silhouettes and its own baked colors/AO.
|
|
329
|
+
exportLods: true,
|
|
330
|
+
exportResolution: 224,
|
|
331
|
+
normalsMode: 'gradient',
|
|
332
|
+
previewResolution: 80,
|
|
333
|
+
removeIslands: true,
|
|
334
|
+
// Dual-contouring vertex placement: planar-cut edges and heightfield
|
|
335
|
+
// ridgelines stay knife-sharp instead of chamfering by a voxel.
|
|
336
|
+
sharpFeatures: true,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
export const ROCKGEN_SETTING_GROUPS = Object.freeze([
|
|
340
|
+
Object.freeze({
|
|
341
|
+
description: 'Base primitive the rock piece is displaced from.',
|
|
342
|
+
id: 'shape',
|
|
343
|
+
label: 'Base Shape',
|
|
344
|
+
}),
|
|
345
|
+
Object.freeze({
|
|
346
|
+
description: 'Eroded landform for the Heightfield shape: relief, strata terracing, and real hydraulic/thermal erosion.',
|
|
347
|
+
id: 'heightfield',
|
|
348
|
+
label: 'Heightfield & Erosion',
|
|
349
|
+
}),
|
|
350
|
+
Object.freeze({
|
|
351
|
+
description: 'FBM displacement that roughens the silhouette.',
|
|
352
|
+
id: 'noise',
|
|
353
|
+
label: 'Surface Noise',
|
|
354
|
+
}),
|
|
355
|
+
Object.freeze({
|
|
356
|
+
description: 'Domain warp that swirls the shape before displacement, breaking symmetry.',
|
|
357
|
+
id: 'warp',
|
|
358
|
+
label: 'Domain Warp',
|
|
359
|
+
}),
|
|
360
|
+
Object.freeze({
|
|
361
|
+
description: 'Seeded flat plane slices — dead-flat faces and sharp edges for slabs, shards, and cliff walls.',
|
|
362
|
+
id: 'cuts',
|
|
363
|
+
label: 'Planar Cuts',
|
|
364
|
+
}),
|
|
365
|
+
Object.freeze({
|
|
366
|
+
description: 'Voronoi border grooves for a fractured look. Carves rounded creases, not flat planes — use Planar Cuts for slab faces.',
|
|
367
|
+
id: 'facet',
|
|
368
|
+
label: 'Voronoi Faceting',
|
|
369
|
+
}),
|
|
370
|
+
Object.freeze({
|
|
371
|
+
description: 'Sparse deep fissures in weathered patches — big Voronoi borders gated by a coverage mask.',
|
|
372
|
+
id: 'cracks',
|
|
373
|
+
label: 'Cracks',
|
|
374
|
+
}),
|
|
375
|
+
Object.freeze({
|
|
376
|
+
description: 'Sedimentary bands / terracing grooves along a tiltable axis.',
|
|
377
|
+
id: 'strata',
|
|
378
|
+
label: 'Strata',
|
|
379
|
+
}),
|
|
380
|
+
Object.freeze({
|
|
381
|
+
description: 'Columnar jointing: vertical prismatic columns with stepped heights (basalt cliffs).',
|
|
382
|
+
id: 'columns',
|
|
383
|
+
label: 'Columns',
|
|
384
|
+
}),
|
|
385
|
+
Object.freeze({
|
|
386
|
+
description: 'Shapes the silhouette: flatten the base, taper the top, pinch the sides.',
|
|
387
|
+
id: 'falloff',
|
|
388
|
+
label: 'Silhouette Falloff',
|
|
389
|
+
}),
|
|
390
|
+
Object.freeze({
|
|
391
|
+
description: 'Procedural albedo layers, baked vertex colors, and SDF ambient occlusion (whole document).',
|
|
392
|
+
id: 'surface',
|
|
393
|
+
label: 'Surface Color & AO',
|
|
394
|
+
}),
|
|
395
|
+
Object.freeze({
|
|
396
|
+
description: 'Grid resolutions and normal mode for preview and export meshing.',
|
|
397
|
+
id: 'meshing',
|
|
398
|
+
label: 'Meshing',
|
|
399
|
+
}),
|
|
400
|
+
]);
|
|
401
|
+
|
|
402
|
+
export const ROCKGEN_SETTING_DEFAULTS = Object.freeze({
|
|
403
|
+
columns: DEFAULT_ROCK_COLUMNS_SETTINGS,
|
|
404
|
+
cracks: DEFAULT_ROCK_CRACKS_SETTINGS,
|
|
405
|
+
cuts: DEFAULT_ROCK_CUTS_SETTINGS,
|
|
406
|
+
facet: DEFAULT_ROCK_FACET_SETTINGS,
|
|
407
|
+
heightfield: DEFAULT_ROCK_HEIGHTFIELD_SETTINGS,
|
|
408
|
+
falloff: DEFAULT_ROCK_FALLOFF_SETTINGS,
|
|
409
|
+
meshing: DEFAULT_ROCKGEN_MESHING_SETTINGS,
|
|
410
|
+
noise: DEFAULT_ROCK_NOISE_SETTINGS,
|
|
411
|
+
shape: DEFAULT_ROCK_SHAPE_SETTINGS,
|
|
412
|
+
strata: DEFAULT_ROCK_STRATA_SETTINGS,
|
|
413
|
+
surface: DEFAULT_ROCK_SURFACE_SETTINGS,
|
|
414
|
+
warp: DEFAULT_ROCK_WARP_SETTINGS,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
/** Groups that live on each piece (the rest belong to the document). */
|
|
418
|
+
export const ROCKGEN_PIECE_GROUP_IDS = Object.freeze(['shape', 'heightfield', 'noise', 'warp', 'cuts', 'facet', 'cracks', 'strata', 'columns', 'falloff']);
|
|
419
|
+
|
|
420
|
+
const SELECT_FIELD_OPTIONS = Object.freeze({
|
|
421
|
+
'heightfield.profile': ROCK_HEIGHTFIELD_PROFILES,
|
|
422
|
+
'meshing.normalsMode': ROCK_NORMALS_MODES,
|
|
423
|
+
'surface.textureStyle': ROCK_SURFACE_TEXTURE_STYLES,
|
|
424
|
+
'shape.type': ROCK_SHAPE_TYPES,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const SELECT_FIELD_OPTION_LABELS = Object.freeze({
|
|
428
|
+
'meshing.normalsMode': Object.freeze({ flat: 'Flat (faceted)', gradient: 'Gradient (smooth)' }),
|
|
429
|
+
'surface.textureStyle': Object.freeze({
|
|
430
|
+
basalt: 'Basalt',
|
|
431
|
+
granite: 'Granite',
|
|
432
|
+
limestone: 'Limestone',
|
|
433
|
+
none: 'None',
|
|
434
|
+
sandstone: 'Sandstone',
|
|
435
|
+
veined: 'Veined',
|
|
436
|
+
}),
|
|
437
|
+
'shape.type': Object.freeze({
|
|
438
|
+
box: 'Box',
|
|
439
|
+
capsule: 'Capsule',
|
|
440
|
+
ellipsoid: 'Ellipsoid',
|
|
441
|
+
heightfield: 'Heightfield (eroded)',
|
|
442
|
+
sketch: 'Sketch (drawn)',
|
|
443
|
+
sphere: 'Sphere',
|
|
444
|
+
}),
|
|
445
|
+
'heightfield.profile': Object.freeze({
|
|
446
|
+
mesa: 'Mesa (plateau)', open: 'Open', ridge: 'Ridge (crest)', slope: 'Slope (hillside)',
|
|
447
|
+
}),
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const COLOR_FIELD_KEYS = Object.freeze(new Set([
|
|
451
|
+
'baseColor',
|
|
452
|
+
'cavityColor',
|
|
453
|
+
'lichenColor',
|
|
454
|
+
'mossColor',
|
|
455
|
+
'stainColor',
|
|
456
|
+
'topColor',
|
|
457
|
+
'veinColor',
|
|
458
|
+
]));
|
|
459
|
+
|
|
460
|
+
const FIELD_LABEL_OVERRIDES = Object.freeze({
|
|
461
|
+
aoRadius: 'AO Radius',
|
|
462
|
+
aoStrength: 'AO Strength',
|
|
463
|
+
bevel: 'Edge Bevel',
|
|
464
|
+
bottomFlatten: 'Flatten Base',
|
|
465
|
+
count: 'Cut Count',
|
|
466
|
+
depth: 'Cut Depth',
|
|
467
|
+
grooveDepth: 'Groove Depth',
|
|
468
|
+
grooveWidth: 'Groove Width',
|
|
469
|
+
heightVariation: 'Height Steps',
|
|
470
|
+
capsuleLength: 'Capsule Length',
|
|
471
|
+
colorNoise: 'Color Noise',
|
|
472
|
+
cornerRadius: 'Corner Radius',
|
|
473
|
+
lichenColor: 'Lichen Color',
|
|
474
|
+
lichenCoverage: 'Lichen Coverage',
|
|
475
|
+
mossColor: 'Moss Color',
|
|
476
|
+
mossCoverage: 'Moss Coverage',
|
|
477
|
+
radialPinch: 'Radial Pinch',
|
|
478
|
+
seedOffset: 'Seed Offset',
|
|
479
|
+
sizeX: 'Size X',
|
|
480
|
+
sizeY: 'Size Y',
|
|
481
|
+
sizeZ: 'Size Z',
|
|
482
|
+
stainColor: 'Stain Color',
|
|
483
|
+
stainStrength: 'Stain Strength',
|
|
484
|
+
textureScale: 'Texture Scale',
|
|
485
|
+
textureStrength: 'Texture Strength',
|
|
486
|
+
textureStyle: 'Texture Style',
|
|
487
|
+
tiltDegrees: 'Tilt (deg)',
|
|
488
|
+
topCoatStrength: 'Top Coat',
|
|
489
|
+
topHeightStart: 'Top Height Start',
|
|
490
|
+
topSlopeStart: 'Top Slope Start',
|
|
491
|
+
terraceSteps: 'Terrace Steps',
|
|
492
|
+
topTaper: 'Taper Top',
|
|
493
|
+
veinColor: 'Vein Color',
|
|
494
|
+
veinStrength: 'Vein Strength',
|
|
495
|
+
verticalBias: 'Vertical Bias',
|
|
496
|
+
warpAmount: 'Band Warp',
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
const FIELD_DESCRIPTIONS = Object.freeze({
|
|
500
|
+
'surface.lichenCoverage': 'Adds pale spotty lichen on exposed faces.',
|
|
501
|
+
'surface.mossCoverage': 'Adds green moss on ledges and damp cavities.',
|
|
502
|
+
'surface.stainStrength': 'Adds rusty mineral staining and rain streaks.',
|
|
503
|
+
'surface.textureScale': 'Scales the procedural grain and patch pattern.',
|
|
504
|
+
'surface.textureStrength': 'Blends the selected procedural rock texture into the baked color.',
|
|
505
|
+
'surface.textureStyle': 'Chooses the procedural albedo pattern baked into vertex colors.',
|
|
506
|
+
'surface.topCoatStrength': 'Strength of the top-facing snow, dust, moss, or highlight coat.',
|
|
507
|
+
'surface.veinStrength': 'Adds thin quartz/mineral seams through the rock.',
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
const FIELD_RANGES = Object.freeze({
|
|
511
|
+
'columns.grooveDepth': { max: 0.5, min: 0, step: 0.005 },
|
|
512
|
+
'columns.grooveWidth': { max: 0.6, min: 0.02, step: 0.01 },
|
|
513
|
+
'columns.heightVariation': { max: 2.5, min: 0, step: 0.01 },
|
|
514
|
+
'columns.scale': { max: 6, min: 0.3, step: 0.05 },
|
|
515
|
+
'cracks.coverage': { max: 1, min: 0, step: 0.01 },
|
|
516
|
+
'cracks.depth': { max: 0.3, min: 0, step: 0.005 },
|
|
517
|
+
'cracks.scale': { max: 4, min: 0.2, step: 0.05 },
|
|
518
|
+
'cracks.width': { max: 0.3, min: 0.02, step: 0.005 },
|
|
519
|
+
'cuts.bevel': { max: 0.25, min: 0, step: 0.005 },
|
|
520
|
+
'cuts.count': { max: 24, min: 1, step: 1 },
|
|
521
|
+
'cuts.depth': { max: 0.8, min: 0.02, step: 0.01 },
|
|
522
|
+
'cuts.seedOffset': { max: 9999, min: 0, step: 1 },
|
|
523
|
+
'cuts.verticalBias': { max: 1, min: 0, step: 0.01 },
|
|
524
|
+
'facet.jitter': { max: 1, min: 0, step: 0.01 },
|
|
525
|
+
'heightfield.droplets': { max: 1, min: 0, step: 0.01 },
|
|
526
|
+
'heightfield.erosion': { max: 1, min: 0, step: 0.01 },
|
|
527
|
+
'heightfield.relief': { max: 1, min: 0, step: 0.01 },
|
|
528
|
+
'heightfield.roughness': { max: 1, min: 0, step: 0.01 },
|
|
529
|
+
'heightfield.seedOffset': { max: 9999, min: 0, step: 1 },
|
|
530
|
+
'heightfield.terrace': { max: 1, min: 0, step: 0.01 },
|
|
531
|
+
'heightfield.terraceSteps': { max: 14, min: 2, step: 1 },
|
|
532
|
+
'heightfield.thermal': { max: 1, min: 0, step: 0.01 },
|
|
533
|
+
'facet.scale': { max: 8, min: 0.3, step: 0.05 },
|
|
534
|
+
'facet.strength': { max: 0.8, min: 0, step: 0.005 },
|
|
535
|
+
'falloff.bottomFlatten': { max: 1, min: 0, step: 0.01 },
|
|
536
|
+
'falloff.radialPinch': { max: 1, min: 0, step: 0.01 },
|
|
537
|
+
'falloff.topTaper': { max: 1, min: 0, step: 0.01 },
|
|
538
|
+
'meshing.exportResolution': { max: 320, min: 32, step: 16 },
|
|
539
|
+
'meshing.previewResolution': { max: 128, min: 24, step: 8 },
|
|
540
|
+
'noise.amplitude': { max: 0.6, min: 0, step: 0.005 },
|
|
541
|
+
'noise.frequency': { max: 8, min: 0.1, step: 0.05 },
|
|
542
|
+
'noise.gain': { max: 0.9, min: 0.1, step: 0.01 },
|
|
543
|
+
'noise.lacunarity': { max: 3.5, min: 1.2, step: 0.05 },
|
|
544
|
+
'noise.octaves': { max: 8, min: 1, step: 1 },
|
|
545
|
+
'noise.seedOffset': { max: 9999, min: 0, step: 1 },
|
|
546
|
+
'shape.capsuleLength': { max: 20, min: 0.1, step: 0.05 },
|
|
547
|
+
'shape.cornerRadius': { max: 1, min: 0, step: 0.01 },
|
|
548
|
+
'shape.sizeX': { max: 20, min: 0.1, step: 0.05 },
|
|
549
|
+
'shape.sizeY': { max: 20, min: 0.1, step: 0.05 },
|
|
550
|
+
'shape.sizeZ': { max: 20, min: 0.1, step: 0.05 },
|
|
551
|
+
'strata.frequency': { max: 12, min: 0.5, step: 0.1 },
|
|
552
|
+
'strata.sharpness': { max: 1, min: 0, step: 0.01 },
|
|
553
|
+
'strata.strength': { max: 0.5, min: 0, step: 0.005 },
|
|
554
|
+
'strata.tiltDegrees': { max: 45, min: -45, step: 1 },
|
|
555
|
+
'strata.warpAmount': { max: 1, min: 0, step: 0.01 },
|
|
556
|
+
'surface.aoRadius': { max: 2, min: 0.05, step: 0.01 },
|
|
557
|
+
'surface.aoStrength': { max: 2, min: 0, step: 0.01 },
|
|
558
|
+
'surface.colorNoise': { max: 0.3, min: 0, step: 0.005 },
|
|
559
|
+
'surface.lichenCoverage': { max: 1, min: 0, step: 0.01 },
|
|
560
|
+
'surface.mossCoverage': { max: 1, min: 0, step: 0.01 },
|
|
561
|
+
'surface.stainStrength': { max: 1, min: 0, step: 0.01 },
|
|
562
|
+
'surface.textureScale': { max: 6, min: 0.1, step: 0.05 },
|
|
563
|
+
'surface.textureStrength': { max: 1, min: 0, step: 0.01 },
|
|
564
|
+
'surface.topCoatStrength': { max: 1, min: 0, step: 0.01 },
|
|
565
|
+
'surface.topHeightStart': { max: 1, min: 0, step: 0.01 },
|
|
566
|
+
'surface.topSlopeStart': { max: 1, min: 0, step: 0.01 },
|
|
567
|
+
'surface.veinStrength': { max: 1, min: 0, step: 0.01 },
|
|
568
|
+
'warp.frequency': { max: 4, min: 0.1, step: 0.05 },
|
|
569
|
+
'warp.strength': { max: 1.5, min: 0, step: 0.01 },
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
function labelFromFieldName(key) {
|
|
573
|
+
if (FIELD_LABEL_OVERRIDES[key]) return FIELD_LABEL_OVERRIDES[key];
|
|
574
|
+
return key
|
|
575
|
+
.replace(/([A-Z])/g, ' $1')
|
|
576
|
+
.replace(/^./, (char) => char.toUpperCase());
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function descriptionForField(group, key) {
|
|
580
|
+
const id = `${group.id}.${key}`;
|
|
581
|
+
if (FIELD_DESCRIPTIONS[id]) return FIELD_DESCRIPTIONS[id];
|
|
582
|
+
const label = labelFromFieldName(key).toLowerCase();
|
|
583
|
+
if (key === 'enabled') return `Turns the ${group.label.toLowerCase()} stage on or off.`;
|
|
584
|
+
return `Adjusts ${label} for ${group.label.toLowerCase()}.`;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function createRockgenFieldMetadata(group, key, value) {
|
|
588
|
+
const id = `${group.id}.${key}`;
|
|
589
|
+
const options = SELECT_FIELD_OPTIONS[id];
|
|
590
|
+
const type = typeof value === 'boolean'
|
|
591
|
+
? 'boolean'
|
|
592
|
+
: options
|
|
593
|
+
? 'select'
|
|
594
|
+
: COLOR_FIELD_KEYS.has(key)
|
|
595
|
+
? 'color'
|
|
596
|
+
: 'number';
|
|
597
|
+
return Object.freeze({
|
|
598
|
+
defaultValue: value,
|
|
599
|
+
description: descriptionForField(group, key),
|
|
600
|
+
group: group.id,
|
|
601
|
+
id,
|
|
602
|
+
key,
|
|
603
|
+
label: labelFromFieldName(key),
|
|
604
|
+
optionLabels: options ? SELECT_FIELD_OPTION_LABELS[id] : undefined,
|
|
605
|
+
options: options ? [...options] : undefined,
|
|
606
|
+
range: type === 'number' ? (FIELD_RANGES[id] ?? { max: 1, min: 0, step: 0.01 }) : undefined,
|
|
607
|
+
serializable: true,
|
|
608
|
+
type,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export const ROCKGEN_SETTING_FIELD_SCHEMA = Object.freeze(Object.fromEntries(
|
|
613
|
+
ROCKGEN_SETTING_GROUPS.map((group) => [
|
|
614
|
+
group.id,
|
|
615
|
+
Object.freeze(Object.fromEntries(
|
|
616
|
+
Object.entries(ROCKGEN_SETTING_DEFAULTS[group.id])
|
|
617
|
+
.map(([key, value]) => [key, createRockgenFieldMetadata(group, key, value)]),
|
|
618
|
+
)),
|
|
619
|
+
]),
|
|
620
|
+
));
|
|
621
|
+
|
|
622
|
+
// --- Coercing creators ------------------------------------------------------
|
|
623
|
+
|
|
624
|
+
function numberOption(value, fallback, { max = Infinity, min = -Infinity } = {}) {
|
|
625
|
+
const number = Number(value);
|
|
626
|
+
if (!Number.isFinite(number)) return fallback;
|
|
627
|
+
return Math.min(Math.max(number, min), max);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function booleanOption(value, fallback) {
|
|
631
|
+
return typeof value === 'boolean' ? value : fallback;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function enumOption(value, allowed, fallback) {
|
|
635
|
+
return allowed.includes(value) ? value : fallback;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function colorOption(value, fallback) {
|
|
639
|
+
if (Array.isArray(value) && value.length >= 3) {
|
|
640
|
+
const channels = value.slice(0, 3).map(Number);
|
|
641
|
+
if (channels.every(Number.isFinite)) {
|
|
642
|
+
return channels.map((channel) => Math.min(Math.max(channel, 0), 1));
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return [...fallback];
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function rangeFor(groupId, key) {
|
|
649
|
+
return FIELD_RANGES[`${groupId}.${key}`];
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function createGroupSettings(groupId, options, coercers) {
|
|
653
|
+
const source = options && typeof options === 'object' ? options : {};
|
|
654
|
+
const defaults = ROCKGEN_SETTING_DEFAULTS[groupId];
|
|
655
|
+
const result = {};
|
|
656
|
+
for (const [key, fallback] of Object.entries(defaults)) {
|
|
657
|
+
const coerce = coercers?.[key];
|
|
658
|
+
if (coerce) {
|
|
659
|
+
result[key] = coerce(source[key], fallback);
|
|
660
|
+
} else if (typeof fallback === 'boolean') {
|
|
661
|
+
result[key] = booleanOption(source[key], fallback);
|
|
662
|
+
} else if (Array.isArray(fallback)) {
|
|
663
|
+
result[key] = colorOption(source[key], fallback);
|
|
664
|
+
} else if (typeof fallback === 'number') {
|
|
665
|
+
result[key] = numberOption(source[key], fallback, rangeFor(groupId, key));
|
|
666
|
+
} else {
|
|
667
|
+
result[key] = source[key] ?? fallback;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return result;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
export function createRockShapeSettings(options = null) {
|
|
674
|
+
return createGroupSettings('shape', options, {
|
|
675
|
+
type: (value, fallback) => enumOption(value, ROCK_SHAPE_TYPES, fallback),
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
export function createRockNoiseSettings(options = null) {
|
|
680
|
+
const settings = createGroupSettings('noise', options);
|
|
681
|
+
settings.octaves = Math.round(settings.octaves);
|
|
682
|
+
settings.seedOffset = Math.round(settings.seedOffset);
|
|
683
|
+
return settings;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export function createRockWarpSettings(options = null) {
|
|
687
|
+
return createGroupSettings('warp', options);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
export function createRockCutsSettings(options = null) {
|
|
691
|
+
const settings = createGroupSettings('cuts', options);
|
|
692
|
+
settings.count = Math.round(settings.count);
|
|
693
|
+
settings.seedOffset = Math.round(settings.seedOffset);
|
|
694
|
+
return settings;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export function createRockFacetSettings(options = null) {
|
|
698
|
+
return createGroupSettings('facet', options);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
export function createRockCracksSettings(options = null) {
|
|
702
|
+
return createGroupSettings('cracks', options);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
export function createRockHeightfieldSettings(options = null) {
|
|
706
|
+
const settings = createGroupSettings('heightfield', options, {
|
|
707
|
+
profile: (value, fallback) => enumOption(value, ROCK_HEIGHTFIELD_PROFILES, fallback),
|
|
708
|
+
});
|
|
709
|
+
settings.seedOffset = Math.round(settings.seedOffset);
|
|
710
|
+
settings.terraceSteps = Math.round(settings.terraceSteps);
|
|
711
|
+
return settings;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export function createRockStrataSettings(options = null) {
|
|
715
|
+
return createGroupSettings('strata', options);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
export function createRockColumnsSettings(options = null) {
|
|
719
|
+
return createGroupSettings('columns', options);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
export function createRockFalloffSettings(options = null) {
|
|
723
|
+
return createGroupSettings('falloff', options);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
export function createRockSurfaceSettings(options = null) {
|
|
727
|
+
return createGroupSettings('surface', options, {
|
|
728
|
+
textureStyle: (value, fallback) => enumOption(value, ROCK_SURFACE_TEXTURE_STYLES, fallback),
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
export function createRockgenMeshingSettings(options = null) {
|
|
733
|
+
const settings = createGroupSettings('meshing', options, {
|
|
734
|
+
normalsMode: (value, fallback) => enumOption(value, ROCK_NORMALS_MODES, fallback),
|
|
735
|
+
});
|
|
736
|
+
settings.exportResolution = Math.round(settings.exportResolution);
|
|
737
|
+
settings.previewResolution = Math.round(settings.previewResolution);
|
|
738
|
+
return settings;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/** All piece-level groups coerced from a partial `{ shape, noise, ... }`. */
|
|
742
|
+
export function createRockPieceSettings(options = null) {
|
|
743
|
+
const source = options && typeof options === 'object' ? options : {};
|
|
744
|
+
return {
|
|
745
|
+
columns: createRockColumnsSettings(source.columns),
|
|
746
|
+
cracks: createRockCracksSettings(source.cracks),
|
|
747
|
+
cuts: createRockCutsSettings(source.cuts),
|
|
748
|
+
facet: createRockFacetSettings(source.facet),
|
|
749
|
+
heightfield: createRockHeightfieldSettings(source.heightfield),
|
|
750
|
+
falloff: createRockFalloffSettings(source.falloff),
|
|
751
|
+
noise: createRockNoiseSettings(source.noise),
|
|
752
|
+
shape: createRockShapeSettings(source.shape),
|
|
753
|
+
strata: createRockStrataSettings(source.strata),
|
|
754
|
+
warp: createRockWarpSettings(source.warp),
|
|
755
|
+
};
|
|
756
|
+
}
|