@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,255 @@
1
+ // Rock document: the JSON-serializable source of truth for one rock /
2
+ // cliff / mountain project. A flat ordered list of SDF pieces (left-folded
3
+ // with their combine ops) plus a global sculpt-edit list — no node graph.
4
+ // `revision` is a runtime dirty counter (never serialized); every mutation
5
+ // helper bumps it so compileDocument's cache and the lab's schedulers can
6
+ // tell stale work from fresh.
7
+
8
+ import {
9
+ createRockPieceSettings,
10
+ createRockSurfaceSettings,
11
+ createRockgenMeshingSettings,
12
+ } from './rockgenSettings.js';
13
+ import { resolveRockgenPreset } from './rockgenPresets.js';
14
+ import { compileDocument } from './sdf/fieldCompiler.js';
15
+
16
+ /** Document type tag stamped on saved rockgen project JSON. */
17
+ export const ROCKGEN_PROJECT_DOCUMENT_TYPE = 'toonlab/rockgen-project';
18
+
19
+ /** Current schema version for rockgen project documents. */
20
+ export const ROCKGEN_PROJECT_SCHEMA_VERSION = 1;
21
+
22
+ const COMBINE_OPS = Object.freeze(['union', 'smoothUnion', 'subtract', 'intersect']);
23
+
24
+ function vector3Option(value, fallback) {
25
+ if (Array.isArray(value) && value.length >= 3) {
26
+ const parts = value.slice(0, 3).map(Number);
27
+ if (parts.every(Number.isFinite)) return parts;
28
+ }
29
+ return [...fallback];
30
+ }
31
+
32
+ function createRockTransform(options = null) {
33
+ const source = options && typeof options === 'object' ? options : {};
34
+ const scale = vector3Option(source.scale, [1, 1, 1]).map((entry) => Math.max(entry, 0.001));
35
+ return {
36
+ position: vector3Option(source.position, [0, 0, 0]),
37
+ rotation: vector3Option(source.rotation, [0, 0, 0]),
38
+ scale,
39
+ };
40
+ }
41
+
42
+ // Drawn outline for 'sketch' shapes: [[x, y], ...] in the piece's local XY
43
+ // plane (3+ points, implicit close). Invalid or missing -> null; the
44
+ // compiler falls back to the ellipsoid until an outline is drawn.
45
+ function outlineOption(value) {
46
+ if (!Array.isArray(value) || value.length < 3) return null;
47
+ const points = [];
48
+ for (const entry of value) {
49
+ if (!Array.isArray(entry) || entry.length < 2) return null;
50
+ const px = Number(entry[0]);
51
+ const py = Number(entry[1]);
52
+ if (!Number.isFinite(px) || !Number.isFinite(py)) return null;
53
+ points.push([px, py]);
54
+ }
55
+ return points;
56
+ }
57
+
58
+ function createCombine(options = null) {
59
+ const source = options && typeof options === 'object' ? options : {};
60
+ return {
61
+ blend: Math.max(Number(source.blend) || 0, 0),
62
+ op: COMBINE_OPS.includes(source.op) ? source.op : 'union',
63
+ };
64
+ }
65
+
66
+ function helperOption(value) {
67
+ if (!value || typeof value !== 'object') return null;
68
+ const kind = String(value.kind ?? '').trim();
69
+ return kind ? { kind } : null;
70
+ }
71
+
72
+ function nextId(prefix, entries) {
73
+ let highest = 0;
74
+ for (const entry of entries) {
75
+ const match = /^\w+-(\d+)$/.exec(String(entry.id ?? ''));
76
+ if (match) highest = Math.max(highest, Number(match[1]));
77
+ }
78
+ return `${prefix}-${highest + 1}`;
79
+ }
80
+
81
+ /**
82
+ * Creates one rock piece. Accepts a registered piece-preset name, or a
83
+ * partial piece object (`{ name, seed, combine, transform, shape, noise,
84
+ * warp, facet, strata, falloff }`). Ids are assigned when the piece is
85
+ * added to a document.
86
+ */
87
+ export function createRockPiece(optionsOrPresetName = null) {
88
+ let source = optionsOrPresetName;
89
+ if (typeof source === 'string') {
90
+ source = resolveRockgenPreset(source).piece;
91
+ }
92
+ source = source && typeof source === 'object' ? source : {};
93
+ const helper = helperOption(source.helper);
94
+ return {
95
+ combine: createCombine(source.combine),
96
+ ...(helper ? { helper } : {}),
97
+ hidden: Boolean(source.hidden),
98
+ id: typeof source.id === 'string' ? source.id : null,
99
+ name: String(source.name ?? 'Rock'),
100
+ outline: outlineOption(source.outline),
101
+ seed: Math.round(Number(source.seed) || 0),
102
+ transform: createRockTransform(source.transform),
103
+ ...createRockPieceSettings(source),
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Creates a rock document. `options` may be a preset name string or
109
+ * `{ seed, preset, name, pieces, sculptEdits, surface, meshing }`.
110
+ * With no explicit pieces, one piece is built from the preset (default
111
+ * 'boulder').
112
+ */
113
+ export function createRockDocument(options = null) {
114
+ const source = typeof options === 'string'
115
+ ? { preset: options }
116
+ : options && typeof options === 'object' ? options : {};
117
+ const preset = resolveRockgenPreset(source.preset);
118
+
119
+ const document = {
120
+ meshing: createRockgenMeshingSettings(source.meshing ?? preset.meshing),
121
+ name: String(source.name ?? preset.label ?? 'Untitled Rock'),
122
+ pieces: [],
123
+ revision: 0,
124
+ schemaVersion: ROCKGEN_PROJECT_SCHEMA_VERSION,
125
+ sculptEdits: [],
126
+ seed: Math.round(Number(source.seed) || 0) >>> 0,
127
+ surface: createRockSurfaceSettings(source.surface ?? preset.surface),
128
+ type: ROCKGEN_PROJECT_DOCUMENT_TYPE,
129
+ };
130
+
131
+ const pieceSources = Array.isArray(source.pieces) && source.pieces.length > 0
132
+ ? source.pieces
133
+ : preset.kind === 'document' && Array.isArray(preset.pieces) && preset.pieces.length > 0
134
+ ? preset.pieces
135
+ : [preset.piece ?? {}];
136
+ for (const pieceSource of pieceSources) {
137
+ addPieceToDocument(document, createRockPiece(pieceSource));
138
+ }
139
+ for (const edit of Array.isArray(source.sculptEdits) ? source.sculptEdits : []) {
140
+ applySculptEdit(document, edit);
141
+ }
142
+ document.revision = 0;
143
+ return document;
144
+ }
145
+
146
+ /** Marks the document dirty after direct settings mutation. */
147
+ export function bumpDocumentRevision(document) {
148
+ document.revision += 1;
149
+ return document.revision;
150
+ }
151
+
152
+ /** Adds a piece (assigning a unique id if needed) and returns it. */
153
+ export function addPieceToDocument(document, piece) {
154
+ if (!piece.id || document.pieces.some((entry) => entry.id === piece.id)) {
155
+ piece.id = nextId('piece', document.pieces);
156
+ }
157
+ document.pieces.push(piece);
158
+ bumpDocumentRevision(document);
159
+ return piece;
160
+ }
161
+
162
+ /** Removes a piece by id; returns true when a piece was removed. */
163
+ export function removePieceFromDocument(document, pieceId) {
164
+ const index = document.pieces.findIndex((entry) => entry.id === pieceId);
165
+ if (index === -1) return false;
166
+ document.pieces.splice(index, 1);
167
+ bumpDocumentRevision(document);
168
+ return true;
169
+ }
170
+
171
+ /** Appends a sculpt edit (assigning a unique id) and returns it. */
172
+ export function applySculptEdit(document, edit) {
173
+ const applied = {
174
+ blend: Math.max(Number(edit.blend) || 0, 0),
175
+ center: vector3Option(edit.center, [0, 0, 0]),
176
+ end: edit.end ? vector3Option(edit.end, [0, 0, 0]) : null,
177
+ id: null,
178
+ radius: Math.max(Number(edit.radius) || 0.1, 0.001),
179
+ shape: edit.shape === 'capsule' ? 'capsule' : 'sphere',
180
+ tool: edit.tool === 'subtract' ? 'subtract' : 'add',
181
+ };
182
+ applied.id = nextId('edit', document.sculptEdits);
183
+ document.sculptEdits.push(applied);
184
+ bumpDocumentRevision(document);
185
+ return applied;
186
+ }
187
+
188
+ /** Removes the most recent sculpt edit; returns it (or null). */
189
+ export function undoLastSculptEdit(document) {
190
+ const edit = document.sculptEdits.pop() ?? null;
191
+ if (edit) bumpDocumentRevision(document);
192
+ return edit;
193
+ }
194
+
195
+ /** World-space AABB of the document's surface: `{ min: [3], max: [3] }`. */
196
+ export function computeDocumentBounds(document) {
197
+ const { bounds } = compileDocument(document, { includeHelpers: false });
198
+ return { max: [...bounds.max], min: [...bounds.min] };
199
+ }
200
+
201
+ /** Serializes a document to JSON (dropping the runtime `revision`). */
202
+ export function serializeRockDocument(document, { pretty = false } = {}) {
203
+ const { revision, ...serializable } = document;
204
+ return JSON.stringify(serializable, null, pretty ? 2 : undefined);
205
+ }
206
+
207
+ // Ordered migrations: index N upgrades a version-N document to N+1.
208
+ // Schema v1 is additive-by-construction (creators default missing fields),
209
+ // so this table stays empty until a breaking change is unavoidable.
210
+ const MIGRATIONS = Object.freeze([]);
211
+
212
+ /**
213
+ * Parses, validates, and coerces a rock document from JSON (string or
214
+ * already-parsed object). Unknown fields are dropped, missing fields get
215
+ * defaults, and older schema versions are migrated. Throws with a
216
+ * descriptive message on structural problems.
217
+ */
218
+ export function deserializeRockDocument(jsonOrObject) {
219
+ let source = jsonOrObject;
220
+ if (typeof source === 'string') {
221
+ try {
222
+ source = JSON.parse(source);
223
+ } catch (error) {
224
+ throw new Error(`Invalid rock document JSON: ${error.message}`);
225
+ }
226
+ }
227
+ if (!source || typeof source !== 'object' || Array.isArray(source)) {
228
+ throw new Error('Rock document must be a JSON object.');
229
+ }
230
+ if (source.type !== ROCKGEN_PROJECT_DOCUMENT_TYPE) {
231
+ throw new Error(`Rock document type must be "${ROCKGEN_PROJECT_DOCUMENT_TYPE}".`);
232
+ }
233
+ let version = Number(source.schemaVersion);
234
+ if (!Number.isFinite(version)) version = ROCKGEN_PROJECT_SCHEMA_VERSION;
235
+ if (version > ROCKGEN_PROJECT_SCHEMA_VERSION) {
236
+ throw new Error(
237
+ `Rock document schema version ${version} is newer than supported version ${ROCKGEN_PROJECT_SCHEMA_VERSION}.`,
238
+ );
239
+ }
240
+ let migrated = source;
241
+ for (; version < ROCKGEN_PROJECT_SCHEMA_VERSION; version += 1) {
242
+ migrated = MIGRATIONS[version - 1](migrated);
243
+ }
244
+ if (!Array.isArray(migrated.pieces) || migrated.pieces.length === 0) {
245
+ throw new Error('Rock document must contain at least one piece.');
246
+ }
247
+ return createRockDocument({
248
+ meshing: migrated.meshing,
249
+ name: migrated.name,
250
+ pieces: migrated.pieces,
251
+ sculptEdits: migrated.sculptEdits,
252
+ seed: migrated.seed,
253
+ surface: migrated.surface,
254
+ });
255
+ }
@@ -0,0 +1,8 @@
1
+ const LEGACY_GROUND_SUPPORT_NAME = /\b(?:Ground Support|Gap(?: Cut)? \d+)\b/;
2
+
3
+ /** Construction pieces used by the lab for authoring helpers, not final rock surface. */
4
+ export function isRockHelperPiece(piece) {
5
+ if (!piece || typeof piece !== 'object') return false;
6
+ if (piece.helper?.kind === 'groundSupport') return true;
7
+ return LEGACY_GROUND_SUPPORT_NAME.test(String(piece.name ?? ''));
8
+ }