@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,356 @@
|
|
|
1
|
+
// First-party stylized erosion for rock-scale heightfields.
|
|
2
|
+
//
|
|
3
|
+
// This model is deliberately not a droplet sim. It treats the heightfield as a
|
|
4
|
+
// drainage graph: flow is routed downhill, high-flow slopes are carved by
|
|
5
|
+
// stream power, flats and concavities collect sediment, then a multi-neighbor
|
|
6
|
+
// thermal pass sheds unstable banks into talus. The outputs match the
|
|
7
|
+
// heightfield patch contract: eroded heights plus flow, erosion, deposition,
|
|
8
|
+
// sediment, and slope masks for stylized surface shading.
|
|
9
|
+
|
|
10
|
+
const SQRT2 = Math.SQRT2;
|
|
11
|
+
|
|
12
|
+
const NEIGHBORS = Object.freeze([
|
|
13
|
+
Object.freeze([-1, 0, 1]),
|
|
14
|
+
Object.freeze([1, 0, 1]),
|
|
15
|
+
Object.freeze([0, -1, 1]),
|
|
16
|
+
Object.freeze([0, 1, 1]),
|
|
17
|
+
Object.freeze([-1, -1, SQRT2]),
|
|
18
|
+
Object.freeze([1, -1, SQRT2]),
|
|
19
|
+
Object.freeze([-1, 1, SQRT2]),
|
|
20
|
+
Object.freeze([1, 1, SQRT2]),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
export const DEFAULT_STYLIZED_EROSION_PARAMS = Object.freeze({
|
|
24
|
+
bankDeposition: 0.5,
|
|
25
|
+
depositionRate: 0.26,
|
|
26
|
+
droplets: 40_000,
|
|
27
|
+
erosionRadius: 2,
|
|
28
|
+
erosionRate: 0.24,
|
|
29
|
+
evaporation: 0.02,
|
|
30
|
+
flowSharpness: 1.45,
|
|
31
|
+
maxPasses: 14,
|
|
32
|
+
minSlope: 0.01,
|
|
33
|
+
seed: 1,
|
|
34
|
+
sedimentCapacity: 3.2,
|
|
35
|
+
smoothing: 0.05,
|
|
36
|
+
strength: 1.0,
|
|
37
|
+
talus: 0.6,
|
|
38
|
+
thermalIterations: 28,
|
|
39
|
+
thermalStrength: 0.36,
|
|
40
|
+
valleyWidening: 0.55,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function clamp01(value) {
|
|
44
|
+
return value < 0 ? 0 : value > 1 ? 1 : value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function clamp(value, min, max) {
|
|
48
|
+
return value < min ? min : value > max ? max : value;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function buildKernel(radius) {
|
|
52
|
+
const r = Math.max(0, Math.round(radius));
|
|
53
|
+
if (r <= 0) {
|
|
54
|
+
return {
|
|
55
|
+
offsets: [[0, 0]],
|
|
56
|
+
weights: [1],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const offsets = [];
|
|
61
|
+
const weights = [];
|
|
62
|
+
let sum = 0;
|
|
63
|
+
const radiusSq = r * r;
|
|
64
|
+
for (let y = -r; y <= r; y += 1) {
|
|
65
|
+
for (let x = -r; x <= r; x += 1) {
|
|
66
|
+
const sq = x * x + y * y;
|
|
67
|
+
if (sq > radiusSq) continue;
|
|
68
|
+
const normalized = Math.sqrt(sq) / Math.max(1, r);
|
|
69
|
+
const weight = (1 - normalized) ** 2;
|
|
70
|
+
offsets.push([x, y]);
|
|
71
|
+
weights.push(weight);
|
|
72
|
+
sum += weight;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (let i = 0; i < weights.length; i += 1) weights[i] /= sum || 1;
|
|
76
|
+
return { offsets, weights };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function applyKernelDelta(map, mask, width, height, x, y, amount, kernel, sign) {
|
|
80
|
+
if (amount <= 0) return 0;
|
|
81
|
+
let applied = 0;
|
|
82
|
+
for (let k = 0; k < kernel.offsets.length; k += 1) {
|
|
83
|
+
const [ox, oy] = kernel.offsets[k];
|
|
84
|
+
const px = x + ox;
|
|
85
|
+
const py = y + oy;
|
|
86
|
+
if (px < 0 || px >= width || py < 0 || py >= height) continue;
|
|
87
|
+
const i = py * width + px;
|
|
88
|
+
const delta = amount * kernel.weights[k];
|
|
89
|
+
if (sign < 0) {
|
|
90
|
+
const removed = Math.min(map[i], delta);
|
|
91
|
+
map[i] -= removed;
|
|
92
|
+
if (mask) mask[i] += removed;
|
|
93
|
+
applied += removed;
|
|
94
|
+
} else {
|
|
95
|
+
map[i] += delta;
|
|
96
|
+
if (mask) mask[i] += delta;
|
|
97
|
+
applied += delta;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return applied;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function normalizeInPlace(array) {
|
|
104
|
+
let max = 1e-6;
|
|
105
|
+
for (let i = 0; i < array.length; i += 1) {
|
|
106
|
+
if (array[i] > max) max = array[i];
|
|
107
|
+
}
|
|
108
|
+
for (let i = 0; i < array.length; i += 1) array[i] /= max;
|
|
109
|
+
return max;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function computeSlopeMap(map, width, height, slopeMap) {
|
|
113
|
+
let slopeMax = 1e-6;
|
|
114
|
+
for (let y = 0; y < height; y += 1) {
|
|
115
|
+
for (let x = 0; x < width; x += 1) {
|
|
116
|
+
const i = y * width + x;
|
|
117
|
+
const xl = x > 0 ? map[i - 1] : map[i];
|
|
118
|
+
const xr = x < width - 1 ? map[i + 1] : map[i];
|
|
119
|
+
const yd = y > 0 ? map[i - width] : map[i];
|
|
120
|
+
const yu = y < height - 1 ? map[i + width] : map[i];
|
|
121
|
+
const slope = Math.hypot((xr - xl) * 0.5, (yu - yd) * 0.5);
|
|
122
|
+
slopeMap[i] = slope;
|
|
123
|
+
if (slope > slopeMax) slopeMax = slope;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return slopeMax;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function localAverage(map, width, height, x, y) {
|
|
130
|
+
let sum = 0;
|
|
131
|
+
let count = 0;
|
|
132
|
+
for (const [dx, dy] of NEIGHBORS) {
|
|
133
|
+
const px = x + dx;
|
|
134
|
+
const py = y + dy;
|
|
135
|
+
if (px < 0 || px >= width || py < 0 || py >= height) continue;
|
|
136
|
+
sum += map[py * width + px];
|
|
137
|
+
count += 1;
|
|
138
|
+
}
|
|
139
|
+
return count > 0 ? sum / count : map[y * width + x];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function routeFlow(map, width, height, order, flow, flowSharpness, evaporation) {
|
|
143
|
+
flow.fill(1);
|
|
144
|
+
order.sort((a, b) => map[b] - map[a] || a - b);
|
|
145
|
+
|
|
146
|
+
const keep = clamp(1 - evaporation * 0.35, 0.75, 1);
|
|
147
|
+
let maxFlow = 1;
|
|
148
|
+
for (const i of order) {
|
|
149
|
+
const x = i % width;
|
|
150
|
+
const y = Math.floor(i / width);
|
|
151
|
+
const h = map[i];
|
|
152
|
+
let weightSum = 0;
|
|
153
|
+
|
|
154
|
+
for (const [dx, dy, dist] of NEIGHBORS) {
|
|
155
|
+
const px = x + dx;
|
|
156
|
+
const py = y + dy;
|
|
157
|
+
if (px < 0 || px >= width || py < 0 || py >= height) continue;
|
|
158
|
+
const drop = (h - map[py * width + px]) / dist;
|
|
159
|
+
if (drop <= 1e-6) continue;
|
|
160
|
+
weightSum += drop ** flowSharpness;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (weightSum > 0) {
|
|
164
|
+
const routed = flow[i] * keep;
|
|
165
|
+
for (const [dx, dy, dist] of NEIGHBORS) {
|
|
166
|
+
const px = x + dx;
|
|
167
|
+
const py = y + dy;
|
|
168
|
+
if (px < 0 || px >= width || py < 0 || py >= height) continue;
|
|
169
|
+
const target = py * width + px;
|
|
170
|
+
const drop = (h - map[target]) / dist;
|
|
171
|
+
if (drop <= 1e-6) continue;
|
|
172
|
+
flow[target] += routed * ((drop ** flowSharpness) / weightSum);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (flow[i] > maxFlow) maxFlow = flow[i];
|
|
177
|
+
}
|
|
178
|
+
return maxFlow;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function hydraulicWeather(map, width, height, p, flowMask, erosionMask, depositionMask) {
|
|
182
|
+
const N = width * height;
|
|
183
|
+
const droplets = Math.max(0, Math.round(p.droplets));
|
|
184
|
+
if (droplets <= 0) return;
|
|
185
|
+
|
|
186
|
+
const intensity = clamp01(droplets / 80_000);
|
|
187
|
+
const passes = Math.max(1, Math.round(4 + Math.sqrt(intensity) * p.maxPasses));
|
|
188
|
+
const order = Array.from({ length: N }, (_, i) => i);
|
|
189
|
+
const flow = new Float32Array(N);
|
|
190
|
+
const slopeMap = new Float32Array(N);
|
|
191
|
+
const channelKernel = buildKernel(Math.max(1, p.erosionRadius * (1 + p.valleyWidening * 0.65)));
|
|
192
|
+
const depositKernel = buildKernel(Math.max(1, p.erosionRadius));
|
|
193
|
+
const erosionScale = 0.0015 + p.erosionRate * 0.012;
|
|
194
|
+
const depositScale = 0.001 + p.depositionRate * 0.006;
|
|
195
|
+
const sedimentScale = clamp(p.sedimentCapacity * 0.25, 0.45, 1.4);
|
|
196
|
+
|
|
197
|
+
for (let pass = 0; pass < passes; pass += 1) {
|
|
198
|
+
const flowMax = routeFlow(map, width, height, order, flow, p.flowSharpness, p.evaporation);
|
|
199
|
+
const slopeMax = computeSlopeMap(map, width, height, slopeMap);
|
|
200
|
+
const logFlowMax = Math.log1p(flowMax);
|
|
201
|
+
const passWeight = 1 - pass / Math.max(1, passes * 1.35);
|
|
202
|
+
|
|
203
|
+
for (let y = 1; y < height - 1; y += 1) {
|
|
204
|
+
for (let x = 1; x < width - 1; x += 1) {
|
|
205
|
+
const i = y * width + x;
|
|
206
|
+
const flowNorm = Math.log1p(flow[i]) / (logFlowMax || 1);
|
|
207
|
+
const slopeNorm = clamp01(slopeMap[i] / slopeMax);
|
|
208
|
+
const channel = (flowNorm ** 1.15) * Math.max(p.minSlope, slopeNorm) ** 0.72;
|
|
209
|
+
const neighborhood = localAverage(map, width, height, x, y);
|
|
210
|
+
const concavity = clamp01((neighborhood - map[i]) * 7);
|
|
211
|
+
const flatness = (1 - slopeNorm) ** 2;
|
|
212
|
+
|
|
213
|
+
const carve = channel * erosionScale * sedimentScale * (0.55 + passWeight * 0.45);
|
|
214
|
+
applyKernelDelta(map, erosionMask, width, height, x, y, carve, channelKernel, -1);
|
|
215
|
+
|
|
216
|
+
const basinDeposit = concavity * p.bankDeposition * depositScale * 1.4;
|
|
217
|
+
const barDeposit = (flowNorm ** 0.9) * flatness * depositScale * sedimentScale;
|
|
218
|
+
applyKernelDelta(
|
|
219
|
+
map,
|
|
220
|
+
depositionMask,
|
|
221
|
+
width,
|
|
222
|
+
height,
|
|
223
|
+
x,
|
|
224
|
+
y,
|
|
225
|
+
(basinDeposit + barDeposit) * (0.65 + passWeight * 0.35),
|
|
226
|
+
depositKernel,
|
|
227
|
+
1,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
flowMask[i] += flowNorm;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function thermalRelax(map, width, height, p, erosionMask, depositionMask) {
|
|
237
|
+
const iterations = Math.max(0, Math.round(p.thermalIterations));
|
|
238
|
+
if (iterations <= 0 || p.thermalStrength <= 0) return;
|
|
239
|
+
|
|
240
|
+
const delta = new Float32Array(width * height);
|
|
241
|
+
const talus = Math.max(0, p.talus);
|
|
242
|
+
const strength = clamp01(p.thermalStrength);
|
|
243
|
+
|
|
244
|
+
for (let iter = 0; iter < iterations; iter += 1) {
|
|
245
|
+
delta.fill(0);
|
|
246
|
+
|
|
247
|
+
for (let y = 1; y < height - 1; y += 1) {
|
|
248
|
+
for (let x = 1; x < width - 1; x += 1) {
|
|
249
|
+
const i = y * width + x;
|
|
250
|
+
const h = map[i];
|
|
251
|
+
let excessSum = 0;
|
|
252
|
+
const transfers = [];
|
|
253
|
+
|
|
254
|
+
for (const [dx, dy, dist] of NEIGHBORS) {
|
|
255
|
+
const px = x + dx;
|
|
256
|
+
const py = y + dy;
|
|
257
|
+
const target = py * width + px;
|
|
258
|
+
const slope = (h - map[target]) / dist;
|
|
259
|
+
if (slope <= talus) continue;
|
|
260
|
+
const excess = (slope - talus) * dist;
|
|
261
|
+
transfers.push([target, excess]);
|
|
262
|
+
excessSum += excess;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (excessSum <= 0) continue;
|
|
266
|
+
const moveTotal = Math.min(h * 0.18, excessSum * 0.22 * strength);
|
|
267
|
+
if (moveTotal <= 0) continue;
|
|
268
|
+
delta[i] -= moveTotal;
|
|
269
|
+
for (const [target, excess] of transfers) {
|
|
270
|
+
delta[target] += moveTotal * (excess / excessSum);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (let i = 0; i < map.length; i += 1) {
|
|
276
|
+
const d = delta[i];
|
|
277
|
+
map[i] += d;
|
|
278
|
+
if (d < 0) erosionMask[i] += -d;
|
|
279
|
+
else if (d > 0) depositionMask[i] += d;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function flowAwareSmooth(map, width, height, amount, flowMask) {
|
|
285
|
+
if (amount <= 0) return;
|
|
286
|
+
const src = Float32Array.from(map);
|
|
287
|
+
let flowMax = 1e-6;
|
|
288
|
+
for (let i = 0; i < flowMask.length; i += 1) {
|
|
289
|
+
if (flowMask[i] > flowMax) flowMax = flowMask[i];
|
|
290
|
+
}
|
|
291
|
+
for (let y = 1; y < height - 1; y += 1) {
|
|
292
|
+
for (let x = 1; x < width - 1; x += 1) {
|
|
293
|
+
const i = y * width + x;
|
|
294
|
+
const avg = (
|
|
295
|
+
src[i]
|
|
296
|
+
+ src[i - 1]
|
|
297
|
+
+ src[i + 1]
|
|
298
|
+
+ src[i - width]
|
|
299
|
+
+ src[i + width]
|
|
300
|
+
) / 5;
|
|
301
|
+
const protectedFlow = clamp01(flowMask[i] / flowMax);
|
|
302
|
+
map[i] = src[i] + (avg - src[i]) * amount * (1 - protectedFlow * 0.65);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Erodes a row-major heightfield with a deterministic drainage/weathering
|
|
309
|
+
* model. Heights are mutated on a copy; the master `strength` blends eroded
|
|
310
|
+
* vs base.
|
|
311
|
+
*
|
|
312
|
+
* @returns {{eroded: Float32Array, flow: Float32Array,
|
|
313
|
+
* erosionMask: Float32Array, depositionMask: Float32Array,
|
|
314
|
+
* sedimentMap: Float32Array, slopeMap: Float32Array}}
|
|
315
|
+
*/
|
|
316
|
+
export function erodeHeightfieldStylized({ width, height, heightmap, params = {} }) {
|
|
317
|
+
const p = { ...DEFAULT_STYLIZED_EROSION_PARAMS, ...params };
|
|
318
|
+
const N = width * height;
|
|
319
|
+
const base = heightmap;
|
|
320
|
+
const map = Float32Array.from(base);
|
|
321
|
+
const flow = new Float32Array(N);
|
|
322
|
+
const erosionMask = new Float32Array(N);
|
|
323
|
+
const depositionMask = new Float32Array(N);
|
|
324
|
+
|
|
325
|
+
hydraulicWeather(map, width, height, p, flow, erosionMask, depositionMask);
|
|
326
|
+
thermalRelax(map, width, height, p, erosionMask, depositionMask);
|
|
327
|
+
flowAwareSmooth(map, width, height, clamp01(p.smoothing), flow);
|
|
328
|
+
|
|
329
|
+
const strength = clamp01(p.strength);
|
|
330
|
+
const eroded = new Float32Array(N);
|
|
331
|
+
const slopeMap = new Float32Array(N);
|
|
332
|
+
const sedimentMap = new Float32Array(N);
|
|
333
|
+
|
|
334
|
+
for (let i = 0; i < N; i += 1) {
|
|
335
|
+
eroded[i] = base[i] + (map[i] - base[i]) * strength;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
computeSlopeMap(eroded, width, height, slopeMap);
|
|
339
|
+
normalizeInPlace(flow);
|
|
340
|
+
normalizeInPlace(erosionMask);
|
|
341
|
+
normalizeInPlace(depositionMask);
|
|
342
|
+
normalizeInPlace(slopeMap);
|
|
343
|
+
|
|
344
|
+
for (let i = 0; i < N; i += 1) {
|
|
345
|
+
sedimentMap[i] = clamp01(depositionMask[i] * 0.85 + flow[i] * (1 - slopeMap[i]) * 0.15);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
depositionMask,
|
|
350
|
+
eroded,
|
|
351
|
+
erosionMask,
|
|
352
|
+
flow,
|
|
353
|
+
sedimentMap,
|
|
354
|
+
slopeMap,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Procedural rock/cliff/mountain generator barrel. Import from
|
|
2
|
+
// '@call-me-sensei/toonlab/rockgen'. Self-contained cluster: depends only on three — the
|
|
3
|
+
// lab composes generated geometry with the environment cluster's toon
|
|
4
|
+
// materials and AO baker (see labs/rock-lab/).
|
|
5
|
+
export * from './rockDocument.js';
|
|
6
|
+
export * from './rockHelpers.js';
|
|
7
|
+
export * from './rockgenPresets.js';
|
|
8
|
+
export * from './rockgenSettings.js';
|
|
9
|
+
export * from './heightfield/heightfieldErosion.js';
|
|
10
|
+
export * from './heightfield/stylizedErosionSim.js';
|
|
11
|
+
export * from './sdf/fieldCompiler.js';
|
|
12
|
+
export * from './sdf/sculptEdits.js';
|
|
13
|
+
export * from './mesh/meshDocument.js';
|
|
14
|
+
export * from './export/glbExport.js';
|