@call-me-sensei/toonlab 0.4.21 → 0.4.23
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/NPM-LIBRARY.md +87 -27
- package/README.md +11 -10
- package/agents/PROMPTS.md +17 -3
- package/agents/README.md +13 -3
- package/agents/claude/CLAUDE.md +3 -0
- package/agents/codex/AGENTS.md +112 -159
- package/agents/cursor/toonlab.mdc +7 -3
- package/agents/examples/game-foundation.mjs +104 -0
- package/agents/references/game-lifecycle.md +57 -0
- package/agents/references/runtime-entry-points.md +26 -0
- package/agents/references/style-bundles.md +4 -1
- package/agents/skills/claude/asset-sourcing/SKILL.md +7 -4
- package/agents/skills/claude/environment/SKILL.md +4 -0
- package/agents/skills/claude/game-dev/SKILL.md +63 -87
- package/agents/skills/claude/lighting/SKILL.md +36 -0
- package/agents/skills/claude/post-processing/SKILL.md +8 -3
- package/agents/skills/claude/rock-ground-shaders/SKILL.md +10 -15
- package/agents/skills/claude/rockgen/SKILL.md +52 -21
- package/agents/skills/claude/scene-style-application/SKILL.md +4 -0
- package/agents/skills/claude/style-presets/SKILL.md +11 -8
- package/agents/skills/claude/toon-shading/SKILL.md +4 -0
- package/agents/skills/claude/vegetation-sky/SKILL.md +13 -7
- package/agents/skills/claude/visual-verification/SKILL.md +45 -119
- package/agents/skills/claude/water/SKILL.md +6 -2
- package/agents/skills/codex/asset-sourcing/SKILL.md +7 -4
- package/agents/skills/codex/environment/SKILL.md +4 -0
- package/agents/skills/codex/game-dev/SKILL.md +63 -87
- package/agents/skills/codex/lighting/SKILL.md +36 -0
- package/agents/skills/codex/post-processing/SKILL.md +8 -3
- package/agents/skills/codex/rock-ground-shaders/SKILL.md +10 -15
- package/agents/skills/codex/rockgen/SKILL.md +52 -21
- package/agents/skills/codex/scene-style-application/SKILL.md +4 -0
- package/agents/skills/codex/style-presets/SKILL.md +11 -8
- package/agents/skills/codex/toon-shading/SKILL.md +4 -0
- package/agents/skills/codex/vegetation-sky/SKILL.md +13 -7
- package/agents/skills/codex/visual-verification/SKILL.md +45 -119
- package/agents/skills/codex/water/SKILL.md +6 -2
- package/database/apply-one-catalog-seed.mjs +63 -0
- package/database/providers.mjs +1 -0
- package/database/seeds/catalog/0005_2026-08-c8-first12.sql +1101 -0
- package/database/seeds/catalog/0006_2026-09-c8-first100.sql +14245 -0
- package/database/seeds/catalog/0007_2026-09-c8-first100-primary-model.sql +14845 -0
- package/mcp/lab-management.mjs +11 -8
- package/mcp/public-catalog.mjs +0 -16
- package/mcp/tree-lab-contract.mjs +2381 -0
- package/mcp/vite-plugin.mjs +5 -1
- package/package.json +29 -18
- package/src/catalog/officialCatalog.js +1 -0
- package/src/catalog/officialCatalogAssetRuntime.js +98 -1
- package/src/catalog/officialCatalogLod.js +160 -16
- package/src/catalog/officialCatalogPlacement.js +52 -12
- package/src/catalog/officialCatalogProvider.js +79 -8
- package/src/catalog/officialCatalogRockPackage.js +172 -0
- package/src/cloud/cloudReprojection.js +40 -7
- package/src/cloud/cloudVolume.js +44 -7
- package/src/cloud/noise/baseShapeVolume.js +8 -6
- package/src/cloud/noise/cirrusMap.js +2 -1
- package/src/cloud/noise/curlNoise.js +2 -1
- package/src/cloud/noise/erosionVolume.js +2 -1
- package/src/cloud/noise/textureCache.js +77 -0
- package/src/cloud/noise/weatherMap.js +2 -1
- package/src/core/sha256.js +83 -0
- package/src/ground-shader/groundShaderMaterial.js +12 -3
- package/src/rock-shader/rockMaterial.js +205 -28
- package/src/rock-shader/rockShaderRuntime.js +17 -1
- package/src/rock-shader/rockShaderSettings.js +16 -0
- package/src/rockgen/index.js +9 -0
- package/src/rockgen/lod/index.js +1 -0
- package/src/rockgen/lod/rockDenseFieldPolicy.js +143 -0
- package/src/rockgen/rockDocument.js +899 -35
- package/src/rockgen/surface/c7GeologySurface.js +39 -9
- package/src/rockgen/surface/naturalRockSurface.js +1948 -0
- package/src/shaders-tsl/chunks/projected-water-caustics.js +14 -2
- package/src/shaders-tsl/chunks/water-foam.js +6 -6
- package/src/shaders-tsl/chunks/water-lighting.js +28 -5
- package/src/shaders-tsl/chunks/water-shore-state.js +5 -2
- package/src/shaders-tsl/chunks/water-waves.js +10 -5
- package/src/shaders-tsl/flower.js +7 -4
- package/src/shaders-tsl/grass.js +4 -3
- package/src/shaders-tsl/water-shore-state-simulation.js +3 -2
- package/src/shaders-tsl/water.js +209 -41
- package/src/sky/skySystem.js +30 -7
- package/src/toon/settings/perspectiveRemovalSettings.js +1 -1
- package/src/toon/toonSettings.js +74 -0
- package/src/vegetation/stylizedFlower.js +6 -0
- package/src/version.js +1 -1
- package/src/water/waterDetailSpectrum.js +91 -0
- package/src/water/waterDynamics.js +422 -0
- package/src/water/waterFoamParticles.js +196 -0
- package/src/water/waterFoamTexture.js +57 -0
- package/src/water/waterHydrodynamics.js +209 -0
- package/src/water/waterMaterial.js +1 -0
- package/src/water/waterRenderExtension.js +107 -0
- package/src/water/waterScenePasses.js +14 -6
- package/src/water/waterSettings.js +53 -26
- package/src/water/waterShoreMaterial.js +38 -14
- package/src/water/waterSpectralOcean.js +137 -0
- package/src/water/waterStageSettings.js +19 -1
- package/src/water/waterSurface.js +62 -18
- package/src/water/waterUnderwaterAtmosphere.js +30 -3
- package/types/catalog/officialCatalog.d.ts +1 -0
- package/types/catalog/officialCatalogPlacement.d.ts +4 -0
- package/types/catalog/officialCatalogRockPackage.d.ts +68 -0
- package/types/cloud/noise/baseShapeVolume.d.ts +1 -1
- package/types/cloud/noise/textureCache.d.ts +12 -0
- package/types/core/sha256.d.ts +2 -0
- package/types/index.d.ts +30 -0
- package/types/rock-shader/rockMaterial.d.ts +3 -0
- package/types/rock-shader/rockShaderSettings.d.ts +2 -0
- package/types/rockgen/index.d.ts +1 -0
- package/types/rockgen/lod/index.d.ts +1 -0
- package/types/rockgen/lod/rockDenseFieldPolicy.d.ts +181 -0
- package/types/rockgen/rockDocument.d.ts +654 -71
- package/types/rockgen/surface/naturalRockSurface.d.ts +100 -0
- package/types/shaders-tsl/chunks/projected-water-caustics.d.ts +7 -1
- package/types/shaders-tsl/chunks/water-lighting.d.ts +4 -3
- package/types/shaders-tsl/water.d.ts +2 -1
- package/types/version.d.ts +1 -1
- package/types/water/waterDetailSpectrum.d.ts +13 -0
- package/types/water/waterDynamics.d.ts +89 -0
- package/types/water/waterFoamParticles.d.ts +32 -0
- package/types/water/waterFoamTexture.d.ts +1 -0
- package/types/water/waterHydrodynamics.d.ts +49 -0
- package/types/water/waterRenderExtension.d.ts +16 -0
- package/types/water/waterScenePasses.d.ts +1 -1
- package/types/water/waterSettings.d.ts +1 -0
- package/types/water/waterSpectralOcean.d.ts +17 -0
- package/types/water/waterStageSettings.d.ts +1 -1
- package/types/water/waterUnderwaterAtmosphere.d.ts +19 -4
package/mcp/vite-plugin.mjs
CHANGED
|
@@ -154,7 +154,10 @@ export function toonlabWorkspacePlugin({ rootDirectory = process.cwd(), workspac
|
|
|
154
154
|
'multiview_to_model',
|
|
155
155
|
'model_segment',
|
|
156
156
|
]);
|
|
157
|
-
if (
|
|
157
|
+
if (kind === 'texture-recipe' && !['gemini', 'openai'].includes(provider)) {
|
|
158
|
+
throw Object.assign(new Error('Texture recipes require Gemini or OpenAI.'), { statusCode: 400 });
|
|
159
|
+
}
|
|
160
|
+
if (kind !== 'texture-recipe' && !imageKinds.has(kind) && !modelKinds.has(kind) && !materialKinds.has(kind)) {
|
|
158
161
|
throw Object.assign(new Error('Unsupported generation kind.'), { statusCode: 400 });
|
|
159
162
|
}
|
|
160
163
|
const providerKinds = {
|
|
@@ -203,6 +206,7 @@ export function toonlabWorkspacePlugin({ rootDirectory = process.cwd(), workspac
|
|
|
203
206
|
);
|
|
204
207
|
const result = await runProvider(provider, {
|
|
205
208
|
...requestBody,
|
|
209
|
+
...(kind === 'texture-recipe' ? { jsonMode: true, responseMimeType: 'application/json', maxOutputTokens: 4096 } : {}),
|
|
206
210
|
kind,
|
|
207
211
|
prompt,
|
|
208
212
|
referenceImages,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@call-me-sensei/toonlab",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.23",
|
|
4
4
|
"description": "Anime-style game toolkit for Three.js with cel shading, vegetation, rocks, water, sky, clouds, procedural textures, and portable Lab artifacts",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Hyperbond Studio PTE. LTD. (Call Me Sensei)",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"src/catalog/officialCatalog.js",
|
|
62
62
|
"src/catalog/officialCatalogLod.js",
|
|
63
63
|
"src/catalog/officialCatalogPlacement.js",
|
|
64
|
+
"src/catalog/officialCatalogRockPackage.js",
|
|
64
65
|
"src/core",
|
|
65
66
|
"src/environment",
|
|
66
67
|
"src/ground-shader",
|
|
@@ -125,7 +126,11 @@
|
|
|
125
126
|
"compose.yaml",
|
|
126
127
|
"NPM-LIBRARY.md",
|
|
127
128
|
"README.md",
|
|
128
|
-
"LICENSE"
|
|
129
|
+
"LICENSE",
|
|
130
|
+
"agents/references/game-lifecycle.md",
|
|
131
|
+
"agents/examples/game-foundation.mjs",
|
|
132
|
+
"agents/skills/codex/lighting",
|
|
133
|
+
"agents/skills/claude/lighting"
|
|
129
134
|
],
|
|
130
135
|
"exports": {
|
|
131
136
|
".": {
|
|
@@ -278,7 +283,8 @@
|
|
|
278
283
|
"golden:capture": "node scripts/capture-golden-fixtures.mjs",
|
|
279
284
|
"golden:compare": "node scripts/compare-golden-captures.mjs",
|
|
280
285
|
"verify:rockgen": "node scripts/verify-rockgen.mjs",
|
|
281
|
-
"verify:rock-
|
|
286
|
+
"verify:rock-dense-field-lod": "node scripts/verify-rock-dense-field-lod.mjs && node scripts/verify-official-catalog-lod.mjs",
|
|
287
|
+
"verify:rock-generation-lab": "node scripts/verify-rock-tool-behavior.mjs",
|
|
282
288
|
"verify:rock-c7-library": "node scripts/verify-rock-c7-surface-library.mjs",
|
|
283
289
|
"verify:rock-lab-lifecycle": "node scripts/verify-rock-lab-lifecycle.mjs",
|
|
284
290
|
"verify:rock-shader": "node scripts/verify-rock-shader.mjs",
|
|
@@ -304,7 +310,8 @@
|
|
|
304
310
|
"verify:fauna": "node scripts/verify-fauna.mjs",
|
|
305
311
|
"verify:ambientfx": "node scripts/verify-ambientfx.mjs",
|
|
306
312
|
"verify:vfxgen": "node scripts/verify-vfxgen.mjs",
|
|
307
|
-
"verify:water": "node scripts/verify-
|
|
313
|
+
"verify:water-experiments": "node scripts/verify-water-experiments.mjs",
|
|
314
|
+
"verify:water": "node scripts/verify-watergen.mjs && node scripts/verify-water-physics.mjs && node scripts/verify-water-dynamics.mjs && node scripts/verify-water-beach-swash.mjs",
|
|
308
315
|
"verify:texgen": "node scripts/verify-texgen.mjs",
|
|
309
316
|
"verify:landscape": "node scripts/verify-landscape.mjs",
|
|
310
317
|
"verify:lighting": "node scripts/verify-lighting.mjs",
|
|
@@ -315,9 +322,9 @@
|
|
|
315
322
|
"verify:lab-asset-provenance": "node scripts/verify-lab-asset-provenance.mjs",
|
|
316
323
|
"verify:lab-preview": "node scripts/verify-lab-preview.mjs",
|
|
317
324
|
"verify:library-preview": "node scripts/verify-library-preview-coverage.mjs",
|
|
318
|
-
"verify:public-labs": "node scripts/verify-public-lab-authoring.mjs",
|
|
319
|
-
"verify:vegetation-labs": "node scripts/verify-vegetation-lab-readiness.mjs",
|
|
320
|
-
"verify:water-lab": "node scripts/verify-water-lab-contract.mjs",
|
|
325
|
+
"verify:public-labs": "node scripts/verify-public-lab-authoring.mjs && node scripts/verify-lab-audit-regressions.mjs && node scripts/verify-lab-editing-regressions.mjs",
|
|
326
|
+
"verify:vegetation-labs": "node scripts/verify-vegetation-lab-readiness.mjs && node scripts/verify-vegetation-preview-runtime.mjs",
|
|
327
|
+
"verify:water-lab": "node scripts/verify-water-lab-contract.mjs && node scripts/verify-water-experiments.mjs",
|
|
321
328
|
"verify:weather": "node scripts/verify-weather.mjs",
|
|
322
329
|
"verify:climate": "node scripts/verify-climate.mjs",
|
|
323
330
|
"verify:atmospheric-condition-lab": "node scripts/verify-atmospheric-condition-lab.mjs",
|
|
@@ -356,15 +363,15 @@
|
|
|
356
363
|
"verify:tree-species-references": "node scripts/verify-tree-species-references.mjs",
|
|
357
364
|
"verify:product-namespace": "node scripts/verify-product-namespace.mjs",
|
|
358
365
|
"verify:skills": "node scripts/verify-agent-skills.mjs",
|
|
359
|
-
"verify:docs": "node scripts/verify-package-documentation.mjs",
|
|
366
|
+
"verify:docs": "node scripts/verify-package-documentation.mjs && node scripts/generate-settings-reference.mjs --check && node scripts/verify-documentation-examples.mjs",
|
|
360
367
|
"verify:api": "node scripts/verify-public-api.mjs",
|
|
361
368
|
"verify:package": "node scripts/verify-package-boundary.mjs",
|
|
362
369
|
"verify:style-contract": "node scripts/verify-style-contract.mjs",
|
|
363
370
|
"verify:style-labels-package": "node scripts/verify-packaged-style-labels.mjs",
|
|
364
371
|
"verify:collision-package": "node scripts/verify-packaged-world-collision.mjs",
|
|
365
|
-
"verify:official-catalog": "node scripts/verify-official-catalog-provider.mjs && node scripts/verify-official-catalog-runtime.mjs && node scripts/verify-official-catalog-lod.mjs && node scripts/verify-official-catalog-placement.mjs && node scripts/verify-playground-official-catalog.mjs && node scripts/verify-packaged-official-catalog.mjs",
|
|
372
|
+
"verify:official-catalog": "node scripts/verify-official-catalog-provider.mjs && node scripts/verify-official-catalog-rock-package.mjs && node scripts/verify-official-catalog-runtime.mjs && node scripts/verify-official-catalog-lod.mjs && node scripts/verify-official-catalog-placement.mjs && node scripts/verify-playground-official-catalog.mjs && node scripts/verify-packaged-official-catalog.mjs",
|
|
366
373
|
"verify:system-style": "node scripts/verify-system-style-policy.mjs",
|
|
367
|
-
"verify:release": "npm run verify:system-style && npm run verify:style-contract && npm run verify:style-labels-package && npm run verify:collision-package && npm run verify:scene-collision && npm run verify:official-catalog && npm run verify:public-labs && npm run verify:live-labs && npm run verify:rock-shader && npm run verify:rock-generation-lab && npm run verify:rock-lab-lifecycle && npm run verify:ground-shader && npm run verify:manufactured-materials && npm run verify:material-labeling:agent && npm run verify:material-labeling:mcp && npm run verify:water-lab && npm run verify:water && npm run verify:vegetation-labs && npm run verify:texgen && npm run verify:cloud-shader && npm run verify:sky-cloud-lab && npm run verify:world-quality && npm run verify:scatter && npm run verify:branch-tree && npm run verify:grass-package && npm run verify:character-package && npm run verify:locomotion-state && npm run verify:ground-stabilizer && npm run verify:water-interaction && npm run verify:walkable-character && npm run verify:playground-walkable && npm run verify:renderer-configuration && npm run verify:scene-scheduler && npm run verify:scene-surface && npm run verify:shared-shadow-receivers && npm run verify:scene-quality && npm run verify:quality-integration && npm run verify:lab-asset-provenance && npm run verify:api && npm run verify:types && npm run verify:mcp && npm run verify:skills && npm run verify:docs && npm run verify:local-database && npm run verify:package",
|
|
374
|
+
"verify:release": "npm run verify:system-style && npm run verify:style-contract && npm run verify:style-labels-package && npm run verify:collision-package && npm run verify:scene-collision && npm run verify:official-catalog && npm run verify:public-labs && npm run verify:live-labs && npm run verify:natural-rock-surfaces && npm run verify:rock-shader && npm run verify:rock-generation-lab && npm run verify:rock-lab-lifecycle && npm run verify:ground-shader && npm run verify:manufactured-materials && npm run verify:material-labeling:agent && npm run verify:material-labeling:mcp && npm run verify:water-lab && npm run verify:water && npm run verify:vegetation-labs && npm run verify:texgen && npm run verify:cloud-shader && npm run verify:sky-cloud-lab && npm run verify:world-quality && npm run verify:scatter && npm run verify:branch-tree && npm run verify:grass-package && npm run verify:character-package && npm run verify:locomotion-state && npm run verify:ground-stabilizer && npm run verify:water-interaction && npm run verify:walkable-character && npm run verify:playground-walkable && npm run verify:renderer-configuration && npm run verify:scene-scheduler && npm run verify:scene-surface && npm run verify:shared-shadow-receivers && npm run verify:scene-quality && npm run verify:quality-integration && npm run verify:lab-asset-provenance && npm run verify:api && npm run verify:types && npm run verify:mcp && npm run verify:skills && npm run verify:docs && npm run verify:local-database && npm run verify:package",
|
|
368
375
|
"verify:generation": "node scripts/verify-generation.mjs",
|
|
369
376
|
"verify:post": "node scripts/verify-post.mjs",
|
|
370
377
|
"verify:camera": "node scripts/verify-camera.mjs",
|
|
@@ -376,7 +383,7 @@
|
|
|
376
383
|
"verify:all": "npm run verify:system-style && npm run verify:product-namespace && npm run verify:generation && npm run verify:post && npm run verify:camera && npm run verify:motion && npm run verify:biome && npm run verify:soundscape && npm run verify:game-feel && npm run verify:world-quality && npm run verify:rock-shader && npm run verify:ground-shader && npm run verify:rockgen && npm run verify:rock-generation-lab && npm run verify:pathgen && npm run verify:propgen && npm run verify:buildinggen && npm run verify:villagegen && npm run verify:fauna && npm run verify:ambientfx && npm run verify:vfxgen && npm run verify:water && npm run verify:landscape && npm run verify:lighting && npm run verify:lighting-lab && npm run verify:live-labs && npm run verify:lab-preview && npm run verify:weather && npm run verify:climate && npm run verify:atmospheric-condition-lab && npm run verify:sky && npm run verify:cloud-shader && npm run verify:sky-cloud-lab && npm run verify:environment-cycle && npm run verify:manufactured-materials && npm run verify:ground-field && npm run verify:grass && npm run verify:grass-package && npm run verify:character-package && npm run verify:vegetation-shader && npm run verify:lab-styles && npm run verify:scatter && npm run verify:branch-tree && npm run verify:collision-package && npm run verify:scene-collision && npm run verify:official-catalog && npm run verify:api && npm run verify:package && npm run verify:catalog && npm run verify:assetlib && npm run verify:mcp && npm run verify:local-generation",
|
|
377
384
|
"verify:catalog": "node scripts/verify-catalog.mjs",
|
|
378
385
|
"verify:assetlib": "node scripts/verify-assetlib.mjs",
|
|
379
|
-
"verify:mcp": "node scripts/verify-mcp.mjs",
|
|
386
|
+
"verify:mcp": "node scripts/sync-mcp-tree-contract.mjs --check && node scripts/verify-mcp.mjs && node scripts/verify-packaged-mcp.mjs",
|
|
380
387
|
"verify:local-database": "node scripts/verify-local-database.mjs",
|
|
381
388
|
"verify:creation-revisions": "node scripts/verify-creation-revisions.mjs",
|
|
382
389
|
"verify:gallery-database": "node scripts/verify-official-gallery-database.mjs",
|
|
@@ -386,20 +393,21 @@
|
|
|
386
393
|
"thumbs:catalog": "node scripts/generate-catalog-thumbs.mjs",
|
|
387
394
|
"thumbs:home": "node scripts/generate-home-shots.mjs",
|
|
388
395
|
"prepublishOnly": "npm run verify:release",
|
|
389
|
-
"verify:scatter": "node scripts/verify-scatter.mjs"
|
|
396
|
+
"verify:scatter": "node scripts/verify-scatter.mjs",
|
|
397
|
+
"verify:natural-rock-surfaces": "node scripts/verify-natural-rock-surfaces.mjs && node scripts/verify-natural-rock-replay.mjs"
|
|
390
398
|
},
|
|
391
399
|
"dependencies": {
|
|
392
400
|
"3d-tiles-renderer": "^0.5.0",
|
|
393
401
|
"pg": "^8.22.0"
|
|
394
402
|
},
|
|
395
403
|
"peerDependencies": {
|
|
396
|
-
"@react-three/fiber": "^9.0.0",
|
|
397
404
|
"@dimforge/rapier3d-compat": "^0.19.0",
|
|
398
405
|
"@pixiv/three-vrm": "^3.5.4",
|
|
406
|
+
"@react-three/fiber": "^9.0.0",
|
|
407
|
+
"react": "^19.0.0",
|
|
399
408
|
"three": "^0.185.1",
|
|
400
|
-
"three-mesh-bvh": "
|
|
401
|
-
"three-stdlib": "^2.36.1"
|
|
402
|
-
"react": "^19.0.0"
|
|
409
|
+
"three-mesh-bvh": "0.9.14",
|
|
410
|
+
"three-stdlib": "^2.36.1"
|
|
403
411
|
},
|
|
404
412
|
"peerDependenciesMeta": {
|
|
405
413
|
"@react-three/fiber": {
|
|
@@ -436,9 +444,12 @@
|
|
|
436
444
|
"react": "^19.2.7",
|
|
437
445
|
"react-dom": "^19.2.7",
|
|
438
446
|
"three": "^0.185.1",
|
|
439
|
-
"three-mesh-bvh": "
|
|
447
|
+
"three-mesh-bvh": "0.9.14",
|
|
440
448
|
"three-stdlib": "^2.36.1",
|
|
441
449
|
"typescript": "^5.9.3",
|
|
442
|
-
"vite": "^5.0.0"
|
|
450
|
+
"vite": "^5.0.0",
|
|
451
|
+
"manifold-3d": "3.5.1",
|
|
452
|
+
"meshoptimizer": "1.1.1",
|
|
453
|
+
"three-bvh-csg": "0.0.18"
|
|
443
454
|
}
|
|
444
455
|
}
|
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
rockSemanticMaterialTextures,
|
|
15
15
|
} from '../rock-shader/rockSemanticMaterialRuntime.js';
|
|
16
16
|
import { OFFICIAL_CATALOG_ASSET_VERSION } from './officialCatalogProvider.js';
|
|
17
|
+
import {
|
|
18
|
+
loadOfficialCatalogRockPackage,
|
|
19
|
+
loadOfficialCatalogRockShaderInput,
|
|
20
|
+
} from './officialCatalogRockPackage.js';
|
|
17
21
|
|
|
18
22
|
const rendererPools = new WeakMap();
|
|
19
23
|
const headlessPools = new Map();
|
|
@@ -141,18 +145,25 @@ async function defaultPrepareTextures(root, asset) {
|
|
|
141
145
|
export function createOfficialCatalogAssetRuntime({
|
|
142
146
|
decoderBasePath = '/',
|
|
143
147
|
loadModel = loadModelAsset,
|
|
148
|
+
loadRockPackage = loadOfficialCatalogRockPackage,
|
|
149
|
+
loadRockShaderInput = loadOfficialCatalogRockShaderInput,
|
|
144
150
|
prepareTextures = defaultPrepareTextures,
|
|
145
151
|
provider,
|
|
146
152
|
renderer = null,
|
|
153
|
+
rockTextureLoader = null,
|
|
147
154
|
transcodersFactory = createModelAssetTranscoders,
|
|
148
155
|
} = {}) {
|
|
149
156
|
if (!provider || typeof provider.getAsset !== 'function') {
|
|
150
157
|
throw new TypeError('Official catalog asset runtime requires a provider with getAsset().');
|
|
151
158
|
}
|
|
152
159
|
if (typeof loadModel !== 'function') throw new TypeError('loadModel must be a function.');
|
|
160
|
+
if (typeof loadRockPackage !== 'function') throw new TypeError('loadRockPackage must be a function.');
|
|
161
|
+
if (typeof loadRockShaderInput !== 'function') throw new TypeError('loadRockShaderInput must be a function.');
|
|
153
162
|
if (typeof prepareTextures !== 'function') throw new TypeError('prepareTextures must be a function.');
|
|
154
163
|
|
|
155
164
|
const assetPromises = new Map();
|
|
165
|
+
const rockPackagePromises = new Map();
|
|
166
|
+
const rockShaderInputPromises = new Map();
|
|
156
167
|
const sourcePromises = new Map();
|
|
157
168
|
const sourceEntries = new Map();
|
|
158
169
|
const handles = new Set();
|
|
@@ -190,6 +201,53 @@ export function createOfficialCatalogAssetRuntime({
|
|
|
190
201
|
return assetPromises.get(id);
|
|
191
202
|
}
|
|
192
203
|
|
|
204
|
+
async function getAsset(assetOrId) {
|
|
205
|
+
assertActive();
|
|
206
|
+
return resolveAsset(assetOrId);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function getRockPackage(assetOrId, options = {}) {
|
|
210
|
+
assertActive();
|
|
211
|
+
const asset = await resolveAsset(assetOrId);
|
|
212
|
+
assertActive();
|
|
213
|
+
const includeManifest = options.includeManifest === true;
|
|
214
|
+
const includeNatureProvenance = options.includeNatureProvenance === true;
|
|
215
|
+
const key = `${asset.identity}|manifest:${includeManifest}|nature:${includeNatureProvenance}`;
|
|
216
|
+
if (!rockPackagePromises.has(key)) {
|
|
217
|
+
const promise = Promise.resolve(loadRockPackage(asset, options)).catch((error) => {
|
|
218
|
+
if (rockPackagePromises.get(key) === promise) rockPackagePromises.delete(key);
|
|
219
|
+
throw error;
|
|
220
|
+
});
|
|
221
|
+
rockPackagePromises.set(key, promise);
|
|
222
|
+
}
|
|
223
|
+
return rockPackagePromises.get(key);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function getRockShaderInput(assetOrId) {
|
|
227
|
+
assertActive();
|
|
228
|
+
const asset = await resolveAsset(assetOrId);
|
|
229
|
+
assertActive();
|
|
230
|
+
if (!asset.packageFiles?.materialConfigUrl) return null;
|
|
231
|
+
const key = asset.identity;
|
|
232
|
+
if (!rockShaderInputPromises.has(key)) {
|
|
233
|
+
const promise = (async () => {
|
|
234
|
+
const rockPackage = await getRockPackage(asset);
|
|
235
|
+
const options = rockTextureLoader ? { textureLoader: rockTextureLoader } : {};
|
|
236
|
+
const input = await loadRockShaderInput(rockPackage, options);
|
|
237
|
+
if (disposed) {
|
|
238
|
+
Object.values(input?.textures ?? {}).forEach((texture) => texture.dispose?.());
|
|
239
|
+
throw new Error('Official catalog asset runtime was disposed while loading rock textures.');
|
|
240
|
+
}
|
|
241
|
+
return input;
|
|
242
|
+
})().catch((error) => {
|
|
243
|
+
if (rockShaderInputPromises.get(key) === promise) rockShaderInputPromises.delete(key);
|
|
244
|
+
throw error;
|
|
245
|
+
});
|
|
246
|
+
rockShaderInputPromises.set(key, promise);
|
|
247
|
+
}
|
|
248
|
+
return rockShaderInputPromises.get(key);
|
|
249
|
+
}
|
|
250
|
+
|
|
193
251
|
async function sourceFor(asset) {
|
|
194
252
|
const key = asset.identity;
|
|
195
253
|
if (!sourcePromises.has(key)) {
|
|
@@ -273,7 +331,26 @@ export function createOfficialCatalogAssetRuntime({
|
|
|
273
331
|
|
|
274
332
|
function evict(assetOrId, { force = false } = {}) {
|
|
275
333
|
const entry = findEntry(assetOrId);
|
|
276
|
-
|
|
334
|
+
const identity = entry?.asset.identity ?? String(assetOrId?.identity ?? '');
|
|
335
|
+
const id = entry?.asset.id ?? String(assetOrId?.id ?? assetOrId ?? '');
|
|
336
|
+
const packageKeys = [...rockPackagePromises.keys()].filter((key) => (
|
|
337
|
+
(identity && key.startsWith(`${identity}|`)) || (!identity && key.includes(`:${id}@`))
|
|
338
|
+
));
|
|
339
|
+
const shaderKeys = [...rockShaderInputPromises.keys()].filter((key) => (
|
|
340
|
+
key === identity || (!identity && key.includes(`:${id}@`))
|
|
341
|
+
));
|
|
342
|
+
if (!entry && packageKeys.length === 0 && shaderKeys.length === 0) return false;
|
|
343
|
+
if (!entry) {
|
|
344
|
+
packageKeys.forEach((key) => rockPackagePromises.delete(key));
|
|
345
|
+
for (const key of shaderKeys) {
|
|
346
|
+
rockShaderInputPromises.get(key)?.then((input) => {
|
|
347
|
+
Object.values(input?.textures ?? {}).forEach((texture) => texture.dispose?.());
|
|
348
|
+
}).catch(() => {});
|
|
349
|
+
rockShaderInputPromises.delete(key);
|
|
350
|
+
}
|
|
351
|
+
assetPromises.delete(id);
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
277
354
|
if (entry.refs > 0 && !force) return false;
|
|
278
355
|
if (force) {
|
|
279
356
|
for (const handle of [...handles]) {
|
|
@@ -284,6 +361,13 @@ export function createOfficialCatalogAssetRuntime({
|
|
|
284
361
|
sourceEntries.delete(entry.asset.identity);
|
|
285
362
|
sourcePromises.delete(entry.asset.identity);
|
|
286
363
|
assetPromises.delete(entry.asset.id);
|
|
364
|
+
packageKeys.forEach((key) => rockPackagePromises.delete(key));
|
|
365
|
+
for (const key of shaderKeys) {
|
|
366
|
+
rockShaderInputPromises.get(key)?.then((input) => {
|
|
367
|
+
Object.values(input?.textures ?? {}).forEach((texture) => texture.dispose?.());
|
|
368
|
+
}).catch(() => {});
|
|
369
|
+
rockShaderInputPromises.delete(key);
|
|
370
|
+
}
|
|
287
371
|
return true;
|
|
288
372
|
}
|
|
289
373
|
|
|
@@ -297,6 +381,8 @@ export function createOfficialCatalogAssetRuntime({
|
|
|
297
381
|
refs: entry.refs,
|
|
298
382
|
}))),
|
|
299
383
|
pendingAssets: sourcePromises.size - sourceEntries.size,
|
|
384
|
+
pendingRockPackages: rockPackagePromises.size,
|
|
385
|
+
cachedRockShaderInputs: rockShaderInputPromises.size,
|
|
300
386
|
});
|
|
301
387
|
}
|
|
302
388
|
|
|
@@ -305,10 +391,18 @@ export function createOfficialCatalogAssetRuntime({
|
|
|
305
391
|
disposed = true;
|
|
306
392
|
for (const handle of [...handles]) handle.release();
|
|
307
393
|
await Promise.allSettled(sourcePromises.values());
|
|
394
|
+
const shaderInputs = await Promise.allSettled(rockShaderInputPromises.values());
|
|
395
|
+
for (const result of shaderInputs) {
|
|
396
|
+
if (result.status === 'fulfilled') {
|
|
397
|
+
Object.values(result.value?.textures ?? {}).forEach((texture) => texture.dispose?.());
|
|
398
|
+
}
|
|
399
|
+
}
|
|
308
400
|
for (const entry of sourceEntries.values()) disposeResources(entry.resources);
|
|
309
401
|
sourceEntries.clear();
|
|
310
402
|
sourcePromises.clear();
|
|
311
403
|
assetPromises.clear();
|
|
404
|
+
rockPackagePromises.clear();
|
|
405
|
+
rockShaderInputPromises.clear();
|
|
312
406
|
transcoderLease?.release();
|
|
313
407
|
transcoderLease = null;
|
|
314
408
|
}
|
|
@@ -317,6 +411,9 @@ export function createOfficialCatalogAssetRuntime({
|
|
|
317
411
|
acquireAsset,
|
|
318
412
|
dispose,
|
|
319
413
|
evict,
|
|
414
|
+
getAsset,
|
|
415
|
+
getRockPackage,
|
|
416
|
+
getRockShaderInput,
|
|
320
417
|
get disposed() { return disposed; },
|
|
321
418
|
provider,
|
|
322
419
|
stats,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Vector3 } from 'three';
|
|
1
|
+
import { Box3, Sphere, Vector3 } from 'three';
|
|
2
2
|
|
|
3
3
|
const DEFAULT_LOD_NAME_PATTERN = /(?:^|[_\s.-])LOD[_\s.-]?(\d+)(?:$|[_\s.-])/iu;
|
|
4
4
|
|
|
@@ -97,16 +97,118 @@ export function selectCatalogLodLevel({
|
|
|
97
97
|
return selected;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
function normalizePixelThresholds(thresholds, levelCount) {
|
|
101
|
+
const count = Math.max(Math.round(Number(levelCount) || 1), 1);
|
|
102
|
+
const source = Array.isArray(thresholds) ? thresholds : [];
|
|
103
|
+
const normalized = [];
|
|
104
|
+
for (let level = 0; level < count; level += 1) {
|
|
105
|
+
const fallback = level === 0 ? 240 : Math.max((normalized[level - 1] ?? 240) * 0.45, 1);
|
|
106
|
+
const value = Number(source[level]);
|
|
107
|
+
normalized.push(Number.isFinite(value) && value > 0 ? value : fallback);
|
|
108
|
+
}
|
|
109
|
+
for (let level = 1; level < normalized.length; level += 1) {
|
|
110
|
+
normalized[level] = Math.min(normalized[level], normalized[level - 1] - 0.001);
|
|
111
|
+
}
|
|
112
|
+
return Object.freeze(normalized);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Resolve an authored LOD from its projected on-screen diameter. A null result
|
|
117
|
+
* is an intentional cull decision, not a missing-asset failure.
|
|
118
|
+
*/
|
|
119
|
+
export function selectCatalogLodLevelByProjectedPixels({
|
|
120
|
+
availableLevels,
|
|
121
|
+
cullBelowPixels = 0,
|
|
122
|
+
currentLevel = null,
|
|
123
|
+
hysteresis = 0,
|
|
124
|
+
pixelThresholds,
|
|
125
|
+
projectedPixels,
|
|
126
|
+
} = {}) {
|
|
127
|
+
const available = [...new Set((availableLevels ?? []).map(finiteLevel).filter((v) => v !== null))]
|
|
128
|
+
.sort((left, right) => left - right);
|
|
129
|
+
if (available.length === 0) return null;
|
|
130
|
+
const pixels = Number(projectedPixels);
|
|
131
|
+
if (!Number.isFinite(pixels) || pixels < 0) {
|
|
132
|
+
throw new RangeError('Projected LOD pixels must be a finite non-negative number.');
|
|
133
|
+
}
|
|
134
|
+
const margin = Number(hysteresis);
|
|
135
|
+
if (!Number.isFinite(margin) || margin < 0 || margin >= 0.5) {
|
|
136
|
+
throw new RangeError('Catalog LOD hysteresis must be a finite ratio in [0, 0.5).');
|
|
137
|
+
}
|
|
138
|
+
const cull = Math.max(Number(cullBelowPixels) || 0, 0);
|
|
139
|
+
const thresholds = normalizePixelThresholds(pixelThresholds, available.at(-1) + 1);
|
|
140
|
+
const current = finiteLevel(currentLevel);
|
|
141
|
+
|
|
142
|
+
if (pixels < cull) {
|
|
143
|
+
if (current !== null && available.includes(current) && margin > 0
|
|
144
|
+
&& pixels >= cull * (1 - margin)) return current;
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let requested = thresholds.length - 1;
|
|
149
|
+
for (let level = 0; level < thresholds.length; level += 1) {
|
|
150
|
+
if (pixels >= thresholds[level]) {
|
|
151
|
+
requested = level;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const selected = available.filter((level) => level <= requested).at(-1) ?? available[0];
|
|
156
|
+
if (current === null || !available.includes(current) || margin === 0 || selected === current) {
|
|
157
|
+
return selected;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (selected > current) {
|
|
161
|
+
const boundary = thresholds[current] ?? cull;
|
|
162
|
+
if (pixels >= boundary * (1 - margin)) return current;
|
|
163
|
+
} else {
|
|
164
|
+
const boundary = thresholds[selected] ?? thresholds[0];
|
|
165
|
+
if (pixels <= boundary * (1 + margin)) return current;
|
|
166
|
+
}
|
|
167
|
+
return selected;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Estimate projected object diameter for perspective and orthographic cameras. */
|
|
171
|
+
export function estimateCatalogProjectedPixels({
|
|
172
|
+
camera,
|
|
173
|
+
distance,
|
|
174
|
+
referenceDiameter,
|
|
175
|
+
viewportHeight,
|
|
176
|
+
} = {}) {
|
|
177
|
+
const pixelsHigh = Number(viewportHeight);
|
|
178
|
+
const diameter = Number(referenceDiameter);
|
|
179
|
+
if (!camera?.isCamera) throw new TypeError('Projected LOD estimation requires a Three.js camera.');
|
|
180
|
+
if (!Number.isFinite(pixelsHigh) || pixelsHigh <= 0) {
|
|
181
|
+
throw new RangeError('Projected LOD estimation requires a positive viewportHeight.');
|
|
182
|
+
}
|
|
183
|
+
if (!Number.isFinite(diameter) || diameter <= 0) {
|
|
184
|
+
throw new RangeError('Projected LOD estimation requires a positive referenceDiameter.');
|
|
185
|
+
}
|
|
186
|
+
if (camera.isOrthographicCamera) {
|
|
187
|
+
const span = Math.abs(Number(camera.top) - Number(camera.bottom)) / Math.max(Number(camera.zoom) || 1, 0.001);
|
|
188
|
+
return span > 0 ? diameter / span * pixelsHigh : Number.POSITIVE_INFINITY;
|
|
189
|
+
}
|
|
190
|
+
if (!camera.isPerspectiveCamera) {
|
|
191
|
+
throw new TypeError('Projected LOD estimation supports perspective and orthographic cameras.');
|
|
192
|
+
}
|
|
193
|
+
const range = Math.max(Number(distance) || 0, 0.0001);
|
|
194
|
+
const zoom = Math.max(Number(camera.zoom) || 1, 0.001);
|
|
195
|
+
const halfFov = Math.atan(Math.tan((Number(camera.fov) || 50) * Math.PI / 360) / zoom);
|
|
196
|
+
return diameter / (2 * range * Math.tan(halfFov)) * pixelsHigh;
|
|
197
|
+
}
|
|
198
|
+
|
|
100
199
|
/**
|
|
101
200
|
* Authored, renderer-independent LOD controller. The host calls update() with
|
|
102
201
|
* either a direct distance or a camera. World scale is removed from camera
|
|
103
202
|
* distance so a uniformly enlarged landmark changes LOD at proportional range.
|
|
104
203
|
*/
|
|
105
204
|
export function createCatalogLodRuntime(root, {
|
|
205
|
+
cullBelowPixels = 0,
|
|
106
206
|
distances = [0, 45, 120],
|
|
107
207
|
hysteresis = 0,
|
|
108
208
|
maxLevel = Number.POSITIVE_INFINITY,
|
|
109
209
|
namePattern = DEFAULT_LOD_NAME_PATTERN,
|
|
210
|
+
pixelThresholds = null,
|
|
211
|
+
referenceDiameter = null,
|
|
110
212
|
} = {}) {
|
|
111
213
|
const bindings = collectCatalogLodBindings(root, { namePattern });
|
|
112
214
|
const availableLevels = Object.freeze(
|
|
@@ -119,27 +221,41 @@ export function createCatalogLodRuntime(root, {
|
|
|
119
221
|
}
|
|
120
222
|
const worldPosition = new Vector3();
|
|
121
223
|
const worldScale = new Vector3();
|
|
224
|
+
const bounds = new Box3();
|
|
225
|
+
const sphere = new Sphere();
|
|
226
|
+
const usesScreenSpace = Array.isArray(pixelThresholds);
|
|
227
|
+
let localReferenceDiameter = Number(referenceDiameter);
|
|
228
|
+
if (!Number.isFinite(localReferenceDiameter) || localReferenceDiameter <= 0) {
|
|
229
|
+
root.updateWorldMatrix?.(true, true);
|
|
230
|
+
bounds.setFromObject(root, true).getBoundingSphere(sphere);
|
|
231
|
+
root.getWorldScale(worldScale);
|
|
232
|
+
const scale = Math.max(Math.abs(worldScale.x), Math.abs(worldScale.y), Math.abs(worldScale.z), 0.001);
|
|
233
|
+
localReferenceDiameter = Math.max(sphere.radius * 2 / scale, 0.001);
|
|
234
|
+
}
|
|
235
|
+
const normalizedPixelThresholds = usesScreenSpace
|
|
236
|
+
? normalizePixelThresholds(pixelThresholds, availableLevels.at(-1) + 1)
|
|
237
|
+
: null;
|
|
122
238
|
let currentLevel = null;
|
|
123
239
|
let disposed = false;
|
|
124
240
|
|
|
125
241
|
function setLevel(level) {
|
|
126
|
-
if (disposed || level
|
|
242
|
+
if (disposed || (level !== null && !availableLevels.includes(level))) return false;
|
|
127
243
|
if (currentLevel === level) return false;
|
|
128
244
|
bindings.forEach((binding) => {
|
|
129
|
-
binding.mesh.visible = binding.level === level;
|
|
245
|
+
binding.mesh.visible = level !== null && binding.level === level;
|
|
130
246
|
});
|
|
131
247
|
currentLevel = level;
|
|
132
248
|
return true;
|
|
133
249
|
}
|
|
134
250
|
|
|
135
251
|
function resolveDistance({ camera = null, distance = null } = {}) {
|
|
252
|
+
root.updateWorldMatrix?.(true, false);
|
|
253
|
+
root.getWorldPosition(worldPosition);
|
|
254
|
+
root.getWorldScale(worldScale);
|
|
136
255
|
if (Number.isFinite(distance)) return Math.max(distance, 0);
|
|
137
256
|
if (!camera?.getWorldPosition && !camera?.position) {
|
|
138
257
|
throw new TypeError('Catalog LOD update requires distance or camera.');
|
|
139
258
|
}
|
|
140
|
-
root.updateWorldMatrix?.(true, false);
|
|
141
|
-
root.getWorldPosition(worldPosition);
|
|
142
|
-
root.getWorldScale(worldScale);
|
|
143
259
|
const cameraPosition = camera.getWorldPosition
|
|
144
260
|
? camera.getWorldPosition(new Vector3())
|
|
145
261
|
: camera.position;
|
|
@@ -149,17 +265,42 @@ export function createCatalogLodRuntime(root, {
|
|
|
149
265
|
|
|
150
266
|
function update(options = {}) {
|
|
151
267
|
if (disposed || availableLevels.length === 0) return null;
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
268
|
+
const hasDirectPixels = Number.isFinite(Number(options.projectedPixels));
|
|
269
|
+
const distance = hasDirectPixels && !Number.isFinite(options.distance) && !options.camera
|
|
270
|
+
? null
|
|
271
|
+
: resolveDistance(options);
|
|
272
|
+
const projectedPixels = hasDirectPixels
|
|
273
|
+
? Math.max(Number(options.projectedPixels), 0)
|
|
274
|
+
: usesScreenSpace
|
|
275
|
+
? estimateCatalogProjectedPixels({
|
|
276
|
+
camera: options.camera,
|
|
277
|
+
distance,
|
|
278
|
+
referenceDiameter: localReferenceDiameter * Math.max(
|
|
279
|
+
Math.abs(worldScale.x), Math.abs(worldScale.y), Math.abs(worldScale.z), 0.001,
|
|
280
|
+
),
|
|
281
|
+
viewportHeight: options.viewportHeight,
|
|
282
|
+
})
|
|
283
|
+
: null;
|
|
284
|
+
let level = usesScreenSpace
|
|
285
|
+
? selectCatalogLodLevelByProjectedPixels({
|
|
286
|
+
availableLevels,
|
|
287
|
+
cullBelowPixels,
|
|
288
|
+
currentLevel,
|
|
289
|
+
hysteresis: hysteresisRatio,
|
|
290
|
+
pixelThresholds: normalizedPixelThresholds,
|
|
291
|
+
projectedPixels,
|
|
292
|
+
})
|
|
293
|
+
: selectCatalogLodLevel({
|
|
294
|
+
availableLevels,
|
|
295
|
+
distance,
|
|
296
|
+
distances: thresholds,
|
|
297
|
+
currentLevel,
|
|
298
|
+
hysteresis: hysteresisRatio,
|
|
299
|
+
maxLevel,
|
|
300
|
+
});
|
|
301
|
+
if (level !== null) level = Math.min(level, Math.max(Number(maxLevel) || 0, 0));
|
|
161
302
|
const changed = setLevel(level);
|
|
162
|
-
return Object.freeze({ changed, distance, level });
|
|
303
|
+
return Object.freeze({ changed, culled: level === null, distance, level, projectedPixels });
|
|
163
304
|
}
|
|
164
305
|
|
|
165
306
|
if (availableLevels.length > 0) setLevel(availableLevels[0]);
|
|
@@ -176,6 +317,9 @@ export function createCatalogLodRuntime(root, {
|
|
|
176
317
|
get disposed() { return disposed; },
|
|
177
318
|
get level() { return currentLevel; },
|
|
178
319
|
setLevel,
|
|
320
|
+
cullBelowPixels: Math.max(Number(cullBelowPixels) || 0, 0),
|
|
321
|
+
pixelThresholds: normalizedPixelThresholds,
|
|
322
|
+
referenceDiameter: localReferenceDiameter,
|
|
179
323
|
thresholds,
|
|
180
324
|
hysteresis: hysteresisRatio,
|
|
181
325
|
update,
|