@developerz.ai/ts23d 0.0.0-stage → 0.2.0
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/.claude/agents/bug-hunter.md +21 -0
- package/.claude/agents/docs-author.md +13 -0
- package/.claude/agents/dx-auditor.md +14 -0
- package/.claude/agents/primitive-author.md +22 -0
- package/.claude/agents/recipe-author.md +36 -0
- package/.claude/agents/visual-reviewer.md +23 -0
- package/.claude/commands/feature.md +28 -0
- package/.claude/commands/new-recipe.md +19 -0
- package/.claude/commands/release.md +14 -0
- package/.claude/commands/verify.md +9 -0
- package/.claude/skills/author-recipe/SKILL.md +93 -0
- package/CHANGELOG.md +90 -0
- package/CLAUDE.md +85 -0
- package/LICENSE +21 -0
- package/README.md +385 -2
- package/dist/chunk-6b05mnmx.js +19 -0
- package/dist/chunk-6b05mnmx.js.map +11 -0
- package/dist/chunk-7rtfjh9f.js +45 -0
- package/dist/chunk-7rtfjh9f.js.map +10 -0
- package/dist/chunk-8j4c28jt.js +4085 -0
- package/dist/chunk-8j4c28jt.js.map +46 -0
- package/dist/chunk-d1fv6hrm.js +2532 -0
- package/dist/chunk-d1fv6hrm.js.map +43 -0
- package/dist/chunk-dbq4qf52.js +902 -0
- package/dist/chunk-dbq4qf52.js.map +25 -0
- package/dist/chunk-drna2rny.js +836 -0
- package/dist/chunk-drna2rny.js.map +17 -0
- package/dist/chunk-dxvypr7y.js +89 -0
- package/dist/chunk-dxvypr7y.js.map +11 -0
- package/dist/chunk-f6yme003.js +1589 -0
- package/dist/chunk-f6yme003.js.map +30 -0
- package/dist/chunk-hg7pf34j.js +465 -0
- package/dist/chunk-hg7pf34j.js.map +19 -0
- package/dist/chunk-jq4e17tj.js +1031 -0
- package/dist/chunk-jq4e17tj.js.map +21 -0
- package/dist/chunk-y63c6zcv.js +2668 -0
- package/dist/chunk-y63c6zcv.js.map +53 -0
- package/dist/cli/args.d.ts +30 -0
- package/dist/cli/bin.d.ts +2 -0
- package/dist/cli/bin.js +348 -0
- package/dist/cli/bin.js.map +15 -0
- package/dist/cli/load.d.ts +2 -0
- package/dist/cli/manifest.d.ts +53 -0
- package/dist/cli/render-cmds.d.ts +12 -0
- package/dist/cli/run.d.ts +1 -0
- package/dist/cli/texture-cache.d.ts +4 -0
- package/dist/core/asset.d.ts +10 -0
- package/dist/core/definition.d.ts +155 -0
- package/dist/core/errors.d.ts +17 -0
- package/dist/core/family.d.ts +23 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +69 -0
- package/dist/core/index.js.map +9 -0
- package/dist/core/mesh.d.ts +35 -0
- package/dist/core/part.d.ts +114 -0
- package/dist/core/png.d.ts +1 -0
- package/dist/core/rng.d.ts +12 -0
- package/dist/core/transform.d.ts +20 -0
- package/dist/core/vec.d.ts +11 -0
- package/dist/core/world.d.ts +16 -0
- package/dist/export/bin-writer.d.ts +16 -0
- package/dist/export/container.d.ts +3 -0
- package/dist/export/credits.d.ts +4 -0
- package/dist/export/glb-options.d.ts +48 -0
- package/dist/export/glb.d.ts +8 -0
- package/dist/export/hierarchy.d.ts +23 -0
- package/dist/export/index.d.ts +5 -0
- package/dist/export/index.js +40 -0
- package/dist/export/index.js.map +9 -0
- package/dist/export/lights.d.ts +8 -0
- package/dist/export/lod-coverage.d.ts +12 -0
- package/dist/export/materials.d.ts +23 -0
- package/dist/export/merge-pivots.d.ts +6 -0
- package/dist/export/metadata.d.ts +21 -0
- package/dist/export/motion.d.ts +9 -0
- package/dist/export/optimize-parts.d.ts +26 -0
- package/dist/export/primitive.d.ts +3 -0
- package/dist/export/tangents.d.ts +2 -0
- package/dist/export/textures.d.ts +22 -0
- package/dist/export/version.d.ts +2 -0
- package/dist/grammar/index.d.ts +2 -0
- package/dist/grammar/index.js +16 -0
- package/dist/grammar/index.js.map +9 -0
- package/dist/grammar/split.d.ts +21 -0
- package/dist/grammar/whyte.d.ts +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +457 -0
- package/dist/index.js.map +9 -0
- package/dist/kinds/building.d.ts +44 -0
- package/dist/kinds/faces.d.ts +27 -0
- package/dist/kinds/head-armour.d.ts +5 -0
- package/dist/kinds/head-beard.d.ts +6 -0
- package/dist/kinds/head-bust.d.ts +5 -0
- package/dist/kinds/head-extras.d.ts +15 -0
- package/dist/kinds/head-face.d.ts +8 -0
- package/dist/kinds/head-garb.d.ts +4 -0
- package/dist/kinds/head-geom.d.ts +7 -0
- package/dist/kinds/head-hair.d.ts +4 -0
- package/dist/kinds/head-layout.d.ts +29 -0
- package/dist/kinds/head-mesh.d.ts +31 -0
- package/dist/kinds/head-mouth.d.ts +4 -0
- package/dist/kinds/head-params.d.ts +116 -0
- package/dist/kinds/head-presets.d.ts +19 -0
- package/dist/kinds/head-rim.d.ts +10 -0
- package/dist/kinds/head-tone.d.ts +10 -0
- package/dist/kinds/head-torso.d.ts +35 -0
- package/dist/kinds/head.d.ts +39 -0
- package/dist/kinds/hull-lines.d.ts +65 -0
- package/dist/kinds/hull-skin.d.ts +6 -0
- package/dist/kinds/hull.d.ts +14 -0
- package/dist/kinds/index.d.ts +16 -0
- package/dist/kinds/index.js +78 -0
- package/dist/kinds/index.js.map +9 -0
- package/dist/kinds/mass.d.ts +15 -0
- package/dist/kinds/openings.d.ts +30 -0
- package/dist/kinds/planned.d.ts +6 -0
- package/dist/kinds/roof-chimney.d.ts +16 -0
- package/dist/kinds/roof-shape.d.ts +64 -0
- package/dist/kinds/roof.d.ts +31 -0
- package/dist/kinds/vegetation-fronds.d.ts +15 -0
- package/dist/kinds/vegetation-impostor.d.ts +9 -0
- package/dist/kinds/vegetation-leaves.d.ts +15 -0
- package/dist/kinds/vegetation-mesh.d.ts +21 -0
- package/dist/kinds/vegetation-params.d.ts +90 -0
- package/dist/kinds/vegetation-presets.d.ts +20 -0
- package/dist/kinds/vegetation-skeleton.d.ts +20 -0
- package/dist/kinds/vegetation.d.ts +42 -0
- package/dist/kinds/vehicle.d.ts +42 -0
- package/dist/material/cc0.d.ts +4 -0
- package/dist/material/define.d.ts +34 -0
- package/dist/material/generator.d.ts +4 -0
- package/dist/material/index.d.ts +9 -0
- package/dist/material/index.js +70 -0
- package/dist/material/index.js.map +9 -0
- package/dist/material/library.d.ts +7 -0
- package/dist/material/material-def.d.ts +30 -0
- package/dist/material/paint/bond.d.ts +3 -0
- package/dist/material/paint/canvas.d.ts +39 -0
- package/dist/material/paint/cell.d.ts +4 -0
- package/dist/material/paint/cells-round.d.ts +5 -0
- package/dist/material/paint/cells.d.ts +7 -0
- package/dist/material/paint/compose.d.ts +5 -0
- package/dist/material/paint/concrete.d.ts +3 -0
- package/dist/material/paint/courses.d.ts +31 -0
- package/dist/material/paint/decode-png.d.ts +6 -0
- package/dist/material/paint/default-sheet.d.ts +4 -0
- package/dist/material/paint/fabric.d.ts +4 -0
- package/dist/material/paint/foliage-broad.d.ts +5 -0
- package/dist/material/paint/foliage-common.d.ts +35 -0
- package/dist/material/paint/foliage-compound.d.ts +4 -0
- package/dist/material/paint/foliage-conifer.d.ts +5 -0
- package/dist/material/paint/foliage-raster.d.ts +26 -0
- package/dist/material/paint/foliage.d.ts +3 -0
- package/dist/material/paint/image-strip.d.ts +4 -0
- package/dist/material/paint/kit.d.ts +5 -0
- package/dist/material/paint/masks.d.ts +3 -0
- package/dist/material/paint/metal.d.ts +3 -0
- package/dist/material/paint/noise.d.ts +9 -0
- package/dist/material/paint/normal.d.ts +8 -0
- package/dist/material/paint/orm.d.ts +8 -0
- package/dist/material/paint/planks.d.ts +3 -0
- package/dist/material/paint/plates.d.ts +3 -0
- package/dist/material/paint/shapes.d.ts +8 -0
- package/dist/material/paint/slate.d.ts +3 -0
- package/dist/material/paint/stamp.d.ts +3 -0
- package/dist/material/paint/strip.d.ts +3 -0
- package/dist/material/paint/surface.d.ts +5 -0
- package/dist/material/paint/types.d.ts +65 -0
- package/dist/material/paint/validate.d.ts +3 -0
- package/dist/material/palette.d.ts +14 -0
- package/dist/material/shader-godot.d.ts +2 -0
- package/dist/material/shader-three.d.ts +2 -0
- package/dist/material/shader.d.ts +46 -0
- package/dist/material/slots.d.ts +5 -0
- package/dist/material/texture-source.d.ts +34 -0
- package/dist/material/trim-sheet.d.ts +38 -0
- package/dist/neural/generator.d.ts +25 -0
- package/dist/neural/images.d.ts +17 -0
- package/dist/neural/index.d.ts +2 -0
- package/dist/neural/index.js +14 -0
- package/dist/neural/index.js.map +9 -0
- package/dist/passes/bake-ao.d.ts +17 -0
- package/dist/passes/grime.d.ts +15 -0
- package/dist/passes/index.d.ts +6 -0
- package/dist/passes/index.js +22 -0
- package/dist/passes/index.js.map +9 -0
- package/dist/passes/livery.d.ts +19 -0
- package/dist/passes/optimize-cache.d.ts +15 -0
- package/dist/passes/optimize-weld.d.ts +15 -0
- package/dist/passes/optimize.d.ts +28 -0
- package/dist/passes/ray-grid.d.ts +23 -0
- package/dist/passes/weighted-normals.d.ts +8 -0
- package/dist/primitives/beam.d.ts +17 -0
- package/dist/primitives/bevel-box.d.ts +13 -0
- package/dist/primitives/box.d.ts +16 -0
- package/dist/primitives/cell.d.ts +20 -0
- package/dist/primitives/check.d.ts +12 -0
- package/dist/primitives/closed.d.ts +11 -0
- package/dist/primitives/erosion.d.ts +22 -0
- package/dist/primitives/font-5x7.d.ts +3 -0
- package/dist/primitives/heightfield.d.ts +16 -0
- package/dist/primitives/index.d.ts +23 -0
- package/dist/primitives/index.js +81 -0
- package/dist/primitives/index.js.map +9 -0
- package/dist/primitives/lathe-facets.d.ts +8 -0
- package/dist/primitives/lathe.d.ts +26 -0
- package/dist/primitives/lattice.d.ts +30 -0
- package/dist/primitives/loft.d.ts +15 -0
- package/dist/primitives/mc-table.d.ts +2 -0
- package/dist/primitives/member.d.ts +19 -0
- package/dist/primitives/noise.d.ts +34 -0
- package/dist/primitives/planar.d.ts +10 -0
- package/dist/primitives/prism.d.ts +13 -0
- package/dist/primitives/sculpt-edit.d.ts +56 -0
- package/dist/primitives/sculpt-tree.d.ts +23 -0
- package/dist/primitives/sculpt.d.ts +27 -0
- package/dist/primitives/sdf-grid.d.ts +17 -0
- package/dist/primitives/sdf-ops.d.ts +37 -0
- package/dist/primitives/sdf.d.ts +21 -0
- package/dist/primitives/strip-uv.d.ts +11 -0
- package/dist/primitives/surface-nets.d.ts +7 -0
- package/dist/primitives/sweep.d.ts +19 -0
- package/dist/primitives/terrain-color.d.ts +30 -0
- package/dist/primitives/terrain-grid.d.ts +36 -0
- package/dist/primitives/text-layout.d.ts +22 -0
- package/dist/primitives/text.d.ts +27 -0
- package/dist/primitives/tiled.d.ts +22 -0
- package/dist/primitives/transport.d.ts +17 -0
- package/dist/primitives/triangulate.d.ts +2 -0
- package/dist/primitives/value-noise.d.ts +10 -0
- package/dist/qa/checks.d.ts +11 -0
- package/dist/qa/clash.d.ts +2 -0
- package/dist/qa/floating.d.ts +5 -0
- package/dist/qa/gates.d.ts +12 -0
- package/dist/qa/geometry.d.ts +21 -0
- package/dist/qa/index.d.ts +12 -0
- package/dist/qa/index.js +78 -0
- package/dist/qa/index.js.map +9 -0
- package/dist/qa/inside.d.ts +12 -0
- package/dist/qa/inspect.d.ts +18 -0
- package/dist/qa/lods.d.ts +12 -0
- package/dist/qa/measures.d.ts +4 -0
- package/dist/qa/occlusion.d.ts +3 -0
- package/dist/qa/sanity.d.ts +7 -0
- package/dist/qa/types.d.ts +12 -0
- package/dist/render/beauty.d.ts +40 -0
- package/dist/render/camera.d.ts +33 -0
- package/dist/render/depth-map.d.ts +38 -0
- package/dist/render/font.d.ts +1 -0
- package/dist/render/ground.d.ts +13 -0
- package/dist/render/index.d.ts +10 -0
- package/dist/render/index.js +47 -0
- package/dist/render/index.js.map +9 -0
- package/dist/render/lighting.d.ts +27 -0
- package/dist/render/lod-strip.d.ts +5 -0
- package/dist/render/metrics.d.ts +37 -0
- package/dist/render/options.d.ts +28 -0
- package/dist/render/photo-match.d.ts +19 -0
- package/dist/render/raster.d.ts +25 -0
- package/dist/render/resolve.d.ts +11 -0
- package/dist/render/review-prompt.d.ts +2 -0
- package/dist/render/review-warnings.d.ts +34 -0
- package/dist/render/review.d.ts +24 -0
- package/dist/render/scene.d.ts +18 -0
- package/dist/render/shade.d.ts +22 -0
- package/dist/render/sheet.d.ts +15 -0
- package/dist/render/texture.d.ts +15 -0
- package/dist/render/thumbnail.d.ts +8 -0
- package/dist/render/vis-buffer.d.ts +18 -0
- package/dist/testing/assert.d.ts +21 -0
- package/dist/testing/compiled.d.ts +15 -0
- package/dist/testing/hash.d.ts +1 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +134 -0
- package/dist/testing/index.js.map +13 -0
- package/dist/testing/snapshot.d.ts +13 -0
- package/docs/README.md +56 -0
- package/docs/architecture/README.md +68 -0
- package/docs/architecture/adding-a-kind.md +44 -0
- package/docs/architecture/adding-a-primitive.md +49 -0
- package/docs/architecture/errors.md +47 -0
- package/docs/architecture/glb-production.md +214 -0
- package/docs/architecture/gltf-capabilities.md +117 -0
- package/docs/architecture/materials.md +151 -0
- package/docs/decisions/001-recipe-compiler-over-ai-meshes.md +27 -0
- package/docs/decisions/002-typescript-not-json.md +22 -0
- package/docs/decisions/003-shared-trim-sheet.md +21 -0
- package/docs/decisions/004-z-up-metres.md +21 -0
- package/docs/decisions/005-zero-runtime-deps.md +21 -0
- package/docs/decisions/006-single-package-subpath-exports.md +20 -0
- package/docs/decisions/007-cpu-contact-sheets.md +21 -0
- package/docs/decisions/008-surface-materials.md +21 -0
- package/docs/decisions/009-gltf-front-axis.md +21 -0
- package/docs/decisions/README.md +15 -0
- package/docs/idea/01-vision.md +117 -0
- package/docs/idea/02-dsl.md +343 -0
- package/docs/idea/03-ir.md +97 -0
- package/docs/idea/04-materials.md +114 -0
- package/docs/idea/05-agent-loop.md +141 -0
- package/docs/idea/06-qa.md +101 -0
- package/docs/idea/07-what-fits.md +45 -0
- package/docs/idea/08-neural-adapters.md +78 -0
- package/docs/idea/09-kinds.md +105 -0
- package/docs/idea/10-sculpting.md +166 -0
- package/docs/idea/11-materials-authoring.md +132 -0
- package/docs/idea/12-blender-bridge.md +136 -0
- package/docs/idea/13-asset-pipeline-research.md +89 -0
- package/docs/landscape.md +49 -0
- package/docs/plans/roadmap.md +139 -0
- package/examples/build-with-bun.ts +41 -0
- package/llms.txt +47 -0
- package/package.json +100 -4
- package/recipes/README.md +38 -0
- package/recipes/asteroid/README.md +46 -0
- package/recipes/asteroid/asteroid.ts +336 -0
- package/recipes/boulders/README.md +28 -0
- package/recipes/boulders/boulders.ts +117 -0
- package/recipes/boxcar/README.md +53 -0
- package/recipes/boxcar/boxcar.ts +1000 -0
- package/recipes/cargo_container_scifi/README.md +52 -0
- package/recipes/cargo_container_scifi/cargo_container_scifi.ts +461 -0
- package/recipes/dropship/README.md +84 -0
- package/recipes/dropship/dropship.ts +1144 -0
- package/recipes/eiffel_tower/README.md +110 -0
- package/recipes/eiffel_tower/eiffel_tower.ts +781 -0
- package/recipes/goblin_head/README.md +75 -0
- package/recipes/goblin_head/goblin_head.ts +38 -0
- package/recipes/human_head/README.md +68 -0
- package/recipes/human_head/human_head.ts +38 -0
- package/recipes/loco_440/README.md +32 -0
- package/recipes/loco_440/loco_440.ts +1404 -0
- package/recipes/material_swatches/README.md +21 -0
- package/recipes/material_swatches/material_swatches.ts +189 -0
- package/recipes/mountain_range/README.md +45 -0
- package/recipes/mountain_range/mountain_range.ts +128 -0
- package/recipes/oak/README.md +30 -0
- package/recipes/oak/oak.ts +19 -0
- package/recipes/orc_head/README.md +78 -0
- package/recipes/orc_head/orc_head.ts +41 -0
- package/recipes/palm/README.md +30 -0
- package/recipes/palm/palm.ts +19 -0
- package/recipes/pine/README.md +29 -0
- package/recipes/pine/pine.ts +19 -0
- package/recipes/signal_gantry/README.md +36 -0
- package/recipes/signal_gantry/signal_gantry.ts +522 -0
- package/recipes/station_halt/README.md +39 -0
- package/recipes/station_halt/station_halt.ts +722 -0
- package/recipes/t34/README.md +44 -0
- package/recipes/t34/t34.ts +1068 -0
- package/recipes/tiger_1/README.md +75 -0
- package/recipes/tiger_1/tiger_1.ts +1370 -0
- package/recipes/water_tower/README.md +40 -0
- package/recipes/water_tower/water_tower.ts +542 -0
- package/src/cli/args.ts +125 -0
- package/src/cli/bin.ts +7 -0
- package/src/cli/load.ts +17 -0
- package/src/cli/manifest.ts +95 -0
- package/src/cli/render-cmds.ts +66 -0
- package/src/cli/run.ts +192 -0
- package/src/cli/texture-cache.ts +24 -0
- package/src/core/asset.ts +129 -0
- package/src/core/definition.ts +156 -0
- package/src/core/errors.ts +50 -0
- package/src/core/family.ts +50 -0
- package/src/core/index.ts +66 -0
- package/src/core/mesh.ts +114 -0
- package/src/core/part.ts +130 -0
- package/src/core/png.ts +49 -0
- package/src/core/rng.ts +49 -0
- package/src/core/transform.ts +90 -0
- package/src/core/vec.ts +26 -0
- package/src/core/world.ts +80 -0
- package/src/export/bin-writer.ts +79 -0
- package/src/export/container.ts +35 -0
- package/src/export/credits.ts +25 -0
- package/src/export/glb-options.ts +53 -0
- package/src/export/glb.ts +187 -0
- package/src/export/hierarchy.ts +122 -0
- package/src/export/index.ts +34 -0
- package/src/export/lights.ts +49 -0
- package/src/export/lod-coverage.ts +71 -0
- package/src/export/materials.ts +126 -0
- package/src/export/merge-pivots.ts +146 -0
- package/src/export/metadata.ts +62 -0
- package/src/export/motion.ts +70 -0
- package/src/export/optimize-parts.ts +80 -0
- package/src/export/primitive.ts +35 -0
- package/src/export/tangents.ts +75 -0
- package/src/export/textures.ts +88 -0
- package/src/export/version.ts +6 -0
- package/src/grammar/index.ts +4 -0
- package/src/grammar/split.ts +87 -0
- package/src/grammar/whyte.ts +20 -0
- package/src/index.ts +14 -0
- package/src/kinds/building.ts +153 -0
- package/src/kinds/faces.ts +138 -0
- package/src/kinds/head-armour.ts +181 -0
- package/src/kinds/head-beard.ts +133 -0
- package/src/kinds/head-bust.ts +184 -0
- package/src/kinds/head-extras.ts +150 -0
- package/src/kinds/head-face.ts +242 -0
- package/src/kinds/head-garb.ts +181 -0
- package/src/kinds/head-geom.ts +31 -0
- package/src/kinds/head-hair.ts +193 -0
- package/src/kinds/head-layout.ts +63 -0
- package/src/kinds/head-mesh.ts +195 -0
- package/src/kinds/head-mouth.ts +111 -0
- package/src/kinds/head-params.ts +162 -0
- package/src/kinds/head-presets.ts +155 -0
- package/src/kinds/head-rim.ts +128 -0
- package/src/kinds/head-tone.ts +52 -0
- package/src/kinds/head-torso.ts +230 -0
- package/src/kinds/head.ts +176 -0
- package/src/kinds/hull-lines.ts +160 -0
- package/src/kinds/hull-skin.ts +83 -0
- package/src/kinds/hull.ts +209 -0
- package/src/kinds/index.ts +63 -0
- package/src/kinds/mass.ts +110 -0
- package/src/kinds/openings.ts +141 -0
- package/src/kinds/planned.ts +12 -0
- package/src/kinds/roof-chimney.ts +100 -0
- package/src/kinds/roof-shape.ts +230 -0
- package/src/kinds/roof.ts +250 -0
- package/src/kinds/vegetation-fronds.ts +126 -0
- package/src/kinds/vegetation-impostor.ts +118 -0
- package/src/kinds/vegetation-leaves.ts +168 -0
- package/src/kinds/vegetation-mesh.ts +174 -0
- package/src/kinds/vegetation-params.ts +147 -0
- package/src/kinds/vegetation-presets.ts +184 -0
- package/src/kinds/vegetation-skeleton.ts +168 -0
- package/src/kinds/vegetation.ts +199 -0
- package/src/kinds/vehicle.ts +144 -0
- package/src/material/cc0.ts +41 -0
- package/src/material/define.ts +130 -0
- package/src/material/generator.ts +63 -0
- package/src/material/index.ts +60 -0
- package/src/material/library.ts +196 -0
- package/src/material/material-def.ts +84 -0
- package/src/material/paint/bond.ts +60 -0
- package/src/material/paint/canvas.ts +101 -0
- package/src/material/paint/cell.ts +39 -0
- package/src/material/paint/cells-round.ts +119 -0
- package/src/material/paint/cells.ts +111 -0
- package/src/material/paint/compose.ts +53 -0
- package/src/material/paint/concrete.ts +62 -0
- package/src/material/paint/courses.ts +85 -0
- package/src/material/paint/decode-png.ts +106 -0
- package/src/material/paint/default-sheet.ts +73 -0
- package/src/material/paint/fabric.ts +63 -0
- package/src/material/paint/foliage-broad.ts +126 -0
- package/src/material/paint/foliage-common.ts +93 -0
- package/src/material/paint/foliage-compound.ts +135 -0
- package/src/material/paint/foliage-conifer.ts +143 -0
- package/src/material/paint/foliage-raster.ts +196 -0
- package/src/material/paint/foliage.ts +23 -0
- package/src/material/paint/image-strip.ts +88 -0
- package/src/material/paint/kit.ts +37 -0
- package/src/material/paint/masks.ts +46 -0
- package/src/material/paint/metal.ts +109 -0
- package/src/material/paint/noise.ts +52 -0
- package/src/material/paint/normal.ts +52 -0
- package/src/material/paint/orm.ts +108 -0
- package/src/material/paint/planks.ts +101 -0
- package/src/material/paint/plates.ts +72 -0
- package/src/material/paint/shapes.ts +28 -0
- package/src/material/paint/slate.ts +52 -0
- package/src/material/paint/stamp.ts +40 -0
- package/src/material/paint/strip.ts +48 -0
- package/src/material/paint/surface.ts +60 -0
- package/src/material/paint/types.ts +94 -0
- package/src/material/paint/validate.ts +74 -0
- package/src/material/palette.ts +46 -0
- package/src/material/shader-godot.ts +77 -0
- package/src/material/shader-three.ts +103 -0
- package/src/material/shader.ts +100 -0
- package/src/material/slots.ts +18 -0
- package/src/material/texture-source.ts +86 -0
- package/src/material/trim-sheet.ts +166 -0
- package/src/neural/generator.ts +37 -0
- package/src/neural/images.ts +27 -0
- package/src/neural/index.ts +10 -0
- package/src/passes/bake-ao.ts +183 -0
- package/src/passes/grime.ts +37 -0
- package/src/passes/index.ts +8 -0
- package/src/passes/livery.ts +66 -0
- package/src/passes/optimize-cache.ts +175 -0
- package/src/passes/optimize-weld.ts +147 -0
- package/src/passes/optimize.ts +85 -0
- package/src/passes/ray-grid.ts +237 -0
- package/src/passes/weighted-normals.ts +125 -0
- package/src/primitives/beam.ts +60 -0
- package/src/primitives/bevel-box.ts +140 -0
- package/src/primitives/box.ts +90 -0
- package/src/primitives/cell.ts +115 -0
- package/src/primitives/check.ts +49 -0
- package/src/primitives/closed.ts +74 -0
- package/src/primitives/erosion.ts +183 -0
- package/src/primitives/font-5x7.ts +117 -0
- package/src/primitives/heightfield.ts +101 -0
- package/src/primitives/index.ts +56 -0
- package/src/primitives/lathe-facets.ts +45 -0
- package/src/primitives/lathe.ts +174 -0
- package/src/primitives/lattice.ts +131 -0
- package/src/primitives/loft.ts +174 -0
- package/src/primitives/mc-table.ts +41 -0
- package/src/primitives/member.ts +130 -0
- package/src/primitives/noise.ts +157 -0
- package/src/primitives/planar.ts +55 -0
- package/src/primitives/prism.ts +85 -0
- package/src/primitives/sculpt-edit.ts +212 -0
- package/src/primitives/sculpt-tree.ts +226 -0
- package/src/primitives/sculpt.ts +80 -0
- package/src/primitives/sdf-grid.ts +131 -0
- package/src/primitives/sdf-ops.ts +145 -0
- package/src/primitives/sdf.ts +83 -0
- package/src/primitives/strip-uv.ts +26 -0
- package/src/primitives/surface-nets.ts +161 -0
- package/src/primitives/sweep.ts +162 -0
- package/src/primitives/terrain-color.ts +85 -0
- package/src/primitives/terrain-grid.ts +137 -0
- package/src/primitives/text-layout.ts +85 -0
- package/src/primitives/text.ts +197 -0
- package/src/primitives/tiled.ts +93 -0
- package/src/primitives/transport.ts +85 -0
- package/src/primitives/triangulate.ts +42 -0
- package/src/primitives/value-noise.ts +62 -0
- package/src/qa/checks.ts +110 -0
- package/src/qa/clash.ts +177 -0
- package/src/qa/floating.ts +63 -0
- package/src/qa/gates.ts +126 -0
- package/src/qa/geometry.ts +187 -0
- package/src/qa/index.ts +28 -0
- package/src/qa/inside.ts +114 -0
- package/src/qa/inspect.ts +107 -0
- package/src/qa/lods.ts +97 -0
- package/src/qa/measures.ts +67 -0
- package/src/qa/occlusion.ts +166 -0
- package/src/qa/sanity.ts +137 -0
- package/src/qa/types.ts +17 -0
- package/src/render/beauty.ts +115 -0
- package/src/render/camera.ts +142 -0
- package/src/render/depth-map.ts +191 -0
- package/src/render/font.ts +76 -0
- package/src/render/ground.ts +87 -0
- package/src/render/index.ts +34 -0
- package/src/render/lighting.ts +121 -0
- package/src/render/lod-strip.ts +34 -0
- package/src/render/metrics.ts +147 -0
- package/src/render/options.ts +81 -0
- package/src/render/photo-match.ts +28 -0
- package/src/render/raster.ts +143 -0
- package/src/render/resolve.ts +109 -0
- package/src/render/review-prompt.ts +59 -0
- package/src/render/review-warnings.ts +92 -0
- package/src/render/review.ts +103 -0
- package/src/render/scene.ts +119 -0
- package/src/render/shade.ts +204 -0
- package/src/render/sheet.ts +88 -0
- package/src/render/texture.ts +97 -0
- package/src/render/thumbnail.ts +75 -0
- package/src/render/vis-buffer.ts +119 -0
- package/src/testing/assert.ts +115 -0
- package/src/testing/compiled.ts +31 -0
- package/src/testing/hash.ts +14 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/snapshot.ts +37 -0
- package/src/types/gltf-validator.d.ts +13 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Shared growth helpers for foliage cells: quadratic stem curves sampled into points, per-leaf
|
|
2
|
+
// colour jitter and the leaf palettes (linear RGB). Allocation is per leaf/stem, never per pixel.
|
|
3
|
+
|
|
4
|
+
import type { Rng } from '../../core/index.ts';
|
|
5
|
+
import type { Foliage, Rgb } from './foliage-raster.ts';
|
|
6
|
+
|
|
7
|
+
/** A quadratic Bézier from p0 via p1 to p2, metres in the cell (anchor usually 0.5, 0.02). */
|
|
8
|
+
export interface Curve {
|
|
9
|
+
readonly x: readonly number[];
|
|
10
|
+
readonly y: readonly number[];
|
|
11
|
+
/** Tangent angle per sample (0 = +V, + toward +U), the convention `Foliage.leaf` takes. */
|
|
12
|
+
readonly a: readonly number[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function curve(
|
|
16
|
+
x0: number,
|
|
17
|
+
y0: number,
|
|
18
|
+
x1: number,
|
|
19
|
+
y1: number,
|
|
20
|
+
x2: number,
|
|
21
|
+
y2: number,
|
|
22
|
+
n = 16,
|
|
23
|
+
): Curve {
|
|
24
|
+
const x: number[] = [];
|
|
25
|
+
const y: number[] = [];
|
|
26
|
+
const a: number[] = [];
|
|
27
|
+
for (let i = 0; i <= n; i++) {
|
|
28
|
+
const t = i / n;
|
|
29
|
+
const u = 1 - t;
|
|
30
|
+
x.push(u * u * x0 + 2 * u * t * x1 + t * t * x2);
|
|
31
|
+
y.push(u * u * y0 + 2 * u * t * y1 + t * t * y2);
|
|
32
|
+
const dx = 2 * u * (x1 - x0) + 2 * t * (x2 - x1);
|
|
33
|
+
const dy = 2 * u * (y1 - y0) + 2 * t * (y2 - y1);
|
|
34
|
+
a.push(Math.atan2(dx, dy));
|
|
35
|
+
}
|
|
36
|
+
return { x, y, a };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Sample index nearest parameter t ∈ [0, 1]. */
|
|
40
|
+
export const at = (c: Curve, t: number): number => Math.round(t * (c.x.length - 1));
|
|
41
|
+
|
|
42
|
+
/** Draw a curve as a tapered stem. */
|
|
43
|
+
export function drawStem(f: Foliage, c: Curve, w0: number, w1: number, col: Rgb): void {
|
|
44
|
+
const n = c.x.length - 1;
|
|
45
|
+
for (let i = 0; i < n; i++) {
|
|
46
|
+
const t0 = i / n;
|
|
47
|
+
const t1 = (i + 1) / n;
|
|
48
|
+
f.stem(
|
|
49
|
+
c.x[i] ?? 0,
|
|
50
|
+
c.y[i] ?? 0,
|
|
51
|
+
c.x[i + 1] ?? 0,
|
|
52
|
+
c.y[i + 1] ?? 0,
|
|
53
|
+
w0 + (w1 - w0) * t0,
|
|
54
|
+
w0 + (w1 - w0) * t1,
|
|
55
|
+
col,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Jitter a colour per leaf: brightness ±amt and a small hue drift between R and G. */
|
|
61
|
+
export function vary(rng: Rng, col: Rgb, amt: number): Rgb {
|
|
62
|
+
const k = 1 + amt * (rng.next() * 2 - 1);
|
|
63
|
+
const drift = 1 + amt * 0.6 * (rng.next() * 2 - 1);
|
|
64
|
+
return [col[0] * k * drift, col[1] * k, (col[2] * k) / drift];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const dim = (col: Rgb, k: number): Rgb => [col[0] * k, col[1] * k, col[2] * k];
|
|
68
|
+
|
|
69
|
+
/** Leaf palettes, linear RGB (multiply by a near-white vertex colour on the card). */
|
|
70
|
+
export const LEAF = {
|
|
71
|
+
beech: [0.09, 0.2, 0.035] as Rgb,
|
|
72
|
+
maple: [0.1, 0.24, 0.04] as Rgb,
|
|
73
|
+
birch: [0.16, 0.3, 0.05] as Rgb,
|
|
74
|
+
acacia: [0.11, 0.2, 0.06] as Rgb,
|
|
75
|
+
palm: [0.12, 0.22, 0.05] as Rgb,
|
|
76
|
+
dry: [0.3, 0.2, 0.08] as Rgb,
|
|
77
|
+
spruce: [0.035, 0.09, 0.04] as Rgb,
|
|
78
|
+
pine: [0.06, 0.13, 0.04] as Rgb,
|
|
79
|
+
willow: [0.2, 0.3, 0.08] as Rgb,
|
|
80
|
+
reed: [0.2, 0.28, 0.08] as Rgb,
|
|
81
|
+
snow: [0.85, 0.88, 0.92] as Rgb,
|
|
82
|
+
bark: [0.08, 0.055, 0.035] as Rgb,
|
|
83
|
+
twig: [0.12, 0.09, 0.05] as Rgb,
|
|
84
|
+
} as const;
|
|
85
|
+
|
|
86
|
+
/** Autumn maple: each leaf picks one of these, then gets jittered. */
|
|
87
|
+
export const AUTUMN: readonly Rgb[] = [
|
|
88
|
+
[0.6, 0.08, 0.02],
|
|
89
|
+
[0.75, 0.25, 0.02],
|
|
90
|
+
[0.8, 0.45, 0.03],
|
|
91
|
+
[0.45, 0.04, 0.03],
|
|
92
|
+
[0.55, 0.35, 0.05],
|
|
93
|
+
];
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Compound and strand foliage cells, all growing from the bottom-centre anchor toward +V:
|
|
2
|
+
// pinnate acacia (rachis, paired pinnae, tiny leaflets), palm frond (arched rachis, lance
|
|
3
|
+
// leaflets both sides, some dry brown tips), willow curtain (long fine strands of narrow leaves;
|
|
4
|
+
// hang it by mapping the card with V down). A dimmed back layer goes down first.
|
|
5
|
+
|
|
6
|
+
import { createRng } from '../../core/index.ts';
|
|
7
|
+
import type { Canvas } from './canvas.ts';
|
|
8
|
+
import { at, curve, dim, drawStem, LEAF, vary } from './foliage-common.ts';
|
|
9
|
+
import { Foliage } from './foliage-raster.ts';
|
|
10
|
+
|
|
11
|
+
export function paintAcacia(c: Canvas, seed: number): void {
|
|
12
|
+
const rng = createRng(seed);
|
|
13
|
+
const f = new Foliage(c);
|
|
14
|
+
const rachis = curve(0.5, 0.02, 0.48 + rng.range(-0.05, 0.05), 0.5, 0.5, 0.95);
|
|
15
|
+
for (let layer = 0; layer < 2; layer++) {
|
|
16
|
+
const k = layer === 0 ? 0.5 : 1;
|
|
17
|
+
if (layer === 1) drawStem(f, rachis, 0.01, 0.003, LEAF.twig);
|
|
18
|
+
const pairs = layer === 0 ? 6 : 8;
|
|
19
|
+
for (let p = 0; p < pairs; p++) {
|
|
20
|
+
const t = 0.15 + (p / pairs) * 0.8 + (layer === 0 ? 0.05 : 0);
|
|
21
|
+
const i = at(rachis, t);
|
|
22
|
+
for (const side of [-1, 1]) {
|
|
23
|
+
const a = (rachis.a[i] ?? 0) + side * rng.range(0.9, 1.2);
|
|
24
|
+
const len = 0.22 * (1 - t * 0.5);
|
|
25
|
+
const bx = rachis.x[i] ?? 0.5;
|
|
26
|
+
const by = rachis.y[i] ?? 0.5;
|
|
27
|
+
const pinna = curve(
|
|
28
|
+
bx,
|
|
29
|
+
by,
|
|
30
|
+
bx + Math.sin(a) * len * 0.5,
|
|
31
|
+
by + Math.cos(a) * len * 0.5 + 0.02,
|
|
32
|
+
bx + Math.sin(a) * len,
|
|
33
|
+
by + Math.cos(a) * len,
|
|
34
|
+
6,
|
|
35
|
+
);
|
|
36
|
+
drawStem(f, pinna, 0.003, 0.0015, dim(LEAF.twig, k));
|
|
37
|
+
for (let q = 1; q <= 12; q++) {
|
|
38
|
+
const j = at(pinna, q / 12.5);
|
|
39
|
+
const col = dim(vary(rng, LEAF.acacia, 0.15), k);
|
|
40
|
+
for (const s2 of [-1, 1])
|
|
41
|
+
f.leaf(
|
|
42
|
+
'ovate',
|
|
43
|
+
pinna.x[j] ?? 0,
|
|
44
|
+
pinna.y[j] ?? 0,
|
|
45
|
+
(pinna.a[j] ?? 0) + s2 * 1.1,
|
|
46
|
+
0.03,
|
|
47
|
+
0.011,
|
|
48
|
+
col,
|
|
49
|
+
0,
|
|
50
|
+
layer * 0.002,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
f.finish(0.5, 0.25);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function paintPalm(c: Canvas, seed: number): void {
|
|
60
|
+
const rng = createRng(seed);
|
|
61
|
+
const f = new Foliage(c);
|
|
62
|
+
const rachis = curve(0.5, 0.02, 0.3, 0.7, 0.78, 0.96, 24);
|
|
63
|
+
for (let layer = 0; layer < 2; layer++) {
|
|
64
|
+
const k = layer === 0 ? 0.55 : 1;
|
|
65
|
+
const n = 26;
|
|
66
|
+
for (let q = 0; q < n; q++) {
|
|
67
|
+
const t = 0.1 + (q / n) * 0.9 + (layer === 0 ? 0.015 : 0);
|
|
68
|
+
const i = at(rachis, Math.min(1, t));
|
|
69
|
+
const bx = rachis.x[i] ?? 0.5;
|
|
70
|
+
const by = rachis.y[i] ?? 0.5;
|
|
71
|
+
for (const side of [-1, 1]) {
|
|
72
|
+
const a = (rachis.a[i] ?? 0) + side * rng.range(0.75, 1.0);
|
|
73
|
+
const len = 0.3 * Math.sin(Math.PI * (0.2 + 0.75 * t)) * rng.range(0.85, 1.05);
|
|
74
|
+
const wid = 0.03;
|
|
75
|
+
f.leaf('lance', bx, by, a, len, wid, dim(vary(rng, LEAF.palm, 0.15), k), 0, layer * 0.002);
|
|
76
|
+
if (rng.next() < 0.3) {
|
|
77
|
+
const tx = bx + Math.sin(a) * len * 0.72;
|
|
78
|
+
const ty = by + Math.cos(a) * len * 0.72;
|
|
79
|
+
f.leaf(
|
|
80
|
+
'blade',
|
|
81
|
+
tx,
|
|
82
|
+
ty,
|
|
83
|
+
a,
|
|
84
|
+
len * 0.28,
|
|
85
|
+
wid * 0.55,
|
|
86
|
+
dim(vary(rng, LEAF.dry, 0.2), k),
|
|
87
|
+
0,
|
|
88
|
+
layer * 0.002 + 0.0002,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (layer === 0) drawStem(f, rachis, 0.018, 0.004, dim(LEAF.palm, 0.8));
|
|
94
|
+
}
|
|
95
|
+
f.finish(0.5, 0.2);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function paintWillow(c: Canvas, seed: number): void {
|
|
99
|
+
const rng = createRng(seed);
|
|
100
|
+
const f = new Foliage(c);
|
|
101
|
+
const strands = 9;
|
|
102
|
+
for (let s = 0; s < strands; s++) {
|
|
103
|
+
const back = s < 3;
|
|
104
|
+
const k = back ? 0.55 : 1;
|
|
105
|
+
const ex = 0.5 + rng.range(-0.4, 0.4);
|
|
106
|
+
const strand = curve(
|
|
107
|
+
0.5 + rng.range(-0.03, 0.03),
|
|
108
|
+
0.02,
|
|
109
|
+
(0.5 + ex) / 2 + rng.range(-0.1, 0.1),
|
|
110
|
+
0.5,
|
|
111
|
+
ex,
|
|
112
|
+
rng.range(0.85, 0.97),
|
|
113
|
+
24,
|
|
114
|
+
);
|
|
115
|
+
drawStem(f, strand, 0.006, 0.0015, dim(LEAF.twig, k));
|
|
116
|
+
for (let q = 2; q < 30; q++) {
|
|
117
|
+
const j = at(strand, q / 30);
|
|
118
|
+
const side = q % 2 === 0 ? 1 : -1;
|
|
119
|
+
const a = (strand.a[j] ?? 0) + side * rng.range(0.2, 0.45);
|
|
120
|
+
const col = dim(vary(rng, LEAF.willow, 0.18), k);
|
|
121
|
+
f.leaf(
|
|
122
|
+
'lance',
|
|
123
|
+
strand.x[j] ?? 0,
|
|
124
|
+
strand.y[j] ?? 0,
|
|
125
|
+
a,
|
|
126
|
+
rng.range(0.07, 0.1),
|
|
127
|
+
0.014,
|
|
128
|
+
col,
|
|
129
|
+
0,
|
|
130
|
+
back ? 0 : 0.002,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
f.finish(0.5, 0.2);
|
|
135
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Needle and blade foliage cells, growing from the bottom-centre anchor toward +V: spruce comb
|
|
2
|
+
// (needle tufts off both sides of an axis and its side shoots), snow-loaded spruce (the same with
|
|
3
|
+
// snow lumps riding the upper side of each shoot), conifer needle spray (pine bundles fanning from
|
|
4
|
+
// twig tips) and reeds/grass (tapering blades, some dry).
|
|
5
|
+
|
|
6
|
+
import { createRng, type Rng } from '../../core/index.ts';
|
|
7
|
+
import type { Canvas } from './canvas.ts';
|
|
8
|
+
import { at, type Curve, curve, dim, drawStem, LEAF, vary } from './foliage-common.ts';
|
|
9
|
+
import { Foliage } from './foliage-raster.ts';
|
|
10
|
+
|
|
11
|
+
/** Needles off both sides of a shoot, angled forward, densest mid-shoot. */
|
|
12
|
+
function comb(f: Foliage, rng: Rng, shoot: Curve, len: number, k: number, lift: number): void {
|
|
13
|
+
const n = shoot.x.length - 1;
|
|
14
|
+
for (let i = 0; i <= n; i++) {
|
|
15
|
+
for (const side of [-1, 1]) {
|
|
16
|
+
const a = (shoot.a[i] ?? 0) + side * rng.range(0.7, 1.0);
|
|
17
|
+
const col = dim(vary(rng, LEAF.spruce, 0.2), k);
|
|
18
|
+
f.leaf(
|
|
19
|
+
'needle',
|
|
20
|
+
shoot.x[i] ?? 0,
|
|
21
|
+
shoot.y[i] ?? 0,
|
|
22
|
+
a,
|
|
23
|
+
len * rng.range(0.8, 1.1),
|
|
24
|
+
0.006,
|
|
25
|
+
col,
|
|
26
|
+
0,
|
|
27
|
+
lift,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function spruce(c: Canvas, seed: number, snow: boolean): void {
|
|
34
|
+
const rng = createRng(seed);
|
|
35
|
+
const f = new Foliage(c);
|
|
36
|
+
const axis = curve(0.5, 0.02, 0.5 + rng.range(-0.04, 0.04), 0.5, 0.5, 0.95, 60);
|
|
37
|
+
const shoots: Curve[] = [];
|
|
38
|
+
for (let s = 0; s < 8; s++) {
|
|
39
|
+
const t = 0.15 + (s / 8) * 0.75;
|
|
40
|
+
const i = at(axis, t);
|
|
41
|
+
const side = s % 2 === 0 ? 1 : -1;
|
|
42
|
+
const bx = axis.x[i] ?? 0.5;
|
|
43
|
+
const by = axis.y[i] ?? 0.5;
|
|
44
|
+
const len = 0.32 * (1 - t * 0.6);
|
|
45
|
+
const a = side * rng.range(0.9, 1.2);
|
|
46
|
+
shoots.push(
|
|
47
|
+
curve(
|
|
48
|
+
bx,
|
|
49
|
+
by,
|
|
50
|
+
bx + Math.sin(a) * len * 0.5,
|
|
51
|
+
by + Math.cos(a) * len * 0.5,
|
|
52
|
+
bx + Math.sin(a) * len,
|
|
53
|
+
by + Math.cos(a) * len + 0.03,
|
|
54
|
+
24,
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
for (const sh of shoots) comb(f, rng, sh, 0.045, 0.55, 0);
|
|
59
|
+
drawStem(f, axis, 0.012, 0.004, LEAF.bark);
|
|
60
|
+
for (const sh of shoots) drawStem(f, sh, 0.005, 0.002, LEAF.bark);
|
|
61
|
+
comb(f, rng, axis, 0.05, 1, 0.002);
|
|
62
|
+
for (const sh of shoots) comb(f, rng, sh, 0.04, 1, 0.002);
|
|
63
|
+
if (snow) {
|
|
64
|
+
for (const sh of [axis, ...shoots]) {
|
|
65
|
+
const n = sh.x.length - 1;
|
|
66
|
+
for (let i = 2; i < n; i += 3) {
|
|
67
|
+
const col = vary(rng, LEAF.snow, 0.05);
|
|
68
|
+
f.leaf(
|
|
69
|
+
'ovate',
|
|
70
|
+
(sh.x[i] ?? 0) - 0.02,
|
|
71
|
+
(sh.y[i] ?? 0) + 0.008,
|
|
72
|
+
Math.PI / 2 + rng.range(-0.3, 0.3),
|
|
73
|
+
rng.range(0.04, 0.07),
|
|
74
|
+
rng.range(0.025, 0.04),
|
|
75
|
+
col,
|
|
76
|
+
0,
|
|
77
|
+
0.006,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
f.finish(0.5, 0.25);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const paintSpruce = (c: Canvas, seed: number): void => spruce(c, seed, false);
|
|
86
|
+
export const paintSpruceSnow = (c: Canvas, seed: number): void => spruce(c, seed, true);
|
|
87
|
+
|
|
88
|
+
export function paintNeedles(c: Canvas, seed: number): void {
|
|
89
|
+
const rng = createRng(seed);
|
|
90
|
+
const f = new Foliage(c);
|
|
91
|
+
const twigs: Curve[] = [];
|
|
92
|
+
for (let s = 0; s < 5; s++) {
|
|
93
|
+
const ex = 0.5 + (s - 2) * 0.13 + rng.range(-0.03, 0.03);
|
|
94
|
+
twigs.push(curve(0.5, 0.02, 0.5 + (ex - 0.5) * 0.4, 0.3, ex, rng.range(0.55, 0.7), 12));
|
|
95
|
+
}
|
|
96
|
+
for (const [s, tw] of twigs.entries()) {
|
|
97
|
+
drawStem(f, tw, 0.012, 0.006, LEAF.bark);
|
|
98
|
+
const k = s % 2 === 0 ? 0.6 : 1;
|
|
99
|
+
// Bundles along the outer third and a full brush at the tip, fanning forward.
|
|
100
|
+
for (let i = 6; i < tw.x.length; i++) {
|
|
101
|
+
const fan = i === tw.x.length - 1 ? 14 : 4;
|
|
102
|
+
for (let q = 0; q < fan; q++) {
|
|
103
|
+
const a = (tw.a[i] ?? 0) + rng.range(-1.1, 1.1) * (fan > 4 ? 0.8 : 1.2);
|
|
104
|
+
const col = dim(vary(rng, LEAF.pine, 0.2), k);
|
|
105
|
+
f.leaf(
|
|
106
|
+
'needle',
|
|
107
|
+
tw.x[i] ?? 0,
|
|
108
|
+
tw.y[i] ?? 0,
|
|
109
|
+
a,
|
|
110
|
+
rng.range(0.12, 0.2),
|
|
111
|
+
0.006,
|
|
112
|
+
col,
|
|
113
|
+
0,
|
|
114
|
+
k * 0.002,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
f.finish(0.5, 0.3);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function paintReeds(c: Canvas, seed: number): void {
|
|
123
|
+
const rng = createRng(seed);
|
|
124
|
+
const f = new Foliage(c);
|
|
125
|
+
for (let b = 0; b < 22; b++) {
|
|
126
|
+
const back = b < 8;
|
|
127
|
+
const x = 0.5 + rng.range(-0.2, 0.2);
|
|
128
|
+
const tilt = (x - 0.5) * 1.2 + rng.range(-0.25, 0.25);
|
|
129
|
+
const col = dim(vary(rng, rng.next() < 0.2 ? LEAF.dry : LEAF.reed, 0.2), back ? 0.55 : 1);
|
|
130
|
+
f.leaf(
|
|
131
|
+
'blade',
|
|
132
|
+
x,
|
|
133
|
+
0.0,
|
|
134
|
+
tilt,
|
|
135
|
+
rng.range(0.55, 0.97),
|
|
136
|
+
rng.range(0.018, 0.032),
|
|
137
|
+
col,
|
|
138
|
+
0,
|
|
139
|
+
back ? 0 : 0.002,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
f.finish(0.5, 0.15);
|
|
143
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Rasteriser for foliage cells: tapered stems and leaf shapes composited back-to-front into
|
|
2
|
+
// linear colour + coverage, with leaf-dome and midrib relief. `finish` writes sRGB RGBA (alpha =
|
|
3
|
+
// leaf mask, transparent texels bled with leaf colour so mips don't halo), height, roughness and
|
|
4
|
+
// an occlusion term that darkens toward the twig axis. Shapes iterate only their pixel bounds.
|
|
5
|
+
|
|
6
|
+
import { type Canvas, clamp01, smooth } from './canvas.ts';
|
|
7
|
+
import { SRGB } from './orm.ts';
|
|
8
|
+
import { segment } from './shapes.ts';
|
|
9
|
+
|
|
10
|
+
export type Rgb = readonly [number, number, number];
|
|
11
|
+
/** Width profile of a leaf along its length t ∈ [0, 1]; 1 = full half-width. */
|
|
12
|
+
export type LeafShape = 'ovate' | 'lance' | 'needle' | 'blade' | 'palmate';
|
|
13
|
+
|
|
14
|
+
const total = (a: Float32Array): number => a.reduce((s, v) => s + v, 0);
|
|
15
|
+
|
|
16
|
+
/** Angle between palmate lobe axes. */
|
|
17
|
+
const LOBE = (Math.PI * 2) / 5;
|
|
18
|
+
|
|
19
|
+
export class Foliage {
|
|
20
|
+
private readonly r: Float32Array;
|
|
21
|
+
private readonly g: Float32Array;
|
|
22
|
+
private readonly b: Float32Array;
|
|
23
|
+
private readonly a: Float32Array;
|
|
24
|
+
private readonly c: Canvas;
|
|
25
|
+
private readonly px: number;
|
|
26
|
+
|
|
27
|
+
constructor(c: Canvas) {
|
|
28
|
+
this.c = c;
|
|
29
|
+
const n = c.w * c.h;
|
|
30
|
+
this.r = new Float32Array(n);
|
|
31
|
+
this.g = new Float32Array(n);
|
|
32
|
+
this.b = new Float32Array(n);
|
|
33
|
+
this.a = new Float32Array(n);
|
|
34
|
+
this.px = c.metresU / c.w;
|
|
35
|
+
c.height.fill(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Composite `cov` of colour (cr, cg, cb) × shade over texel i. */
|
|
39
|
+
private put(i: number, cov: number, col: Rgb, shade: number, h: number, rough: number): void {
|
|
40
|
+
if (cov <= 0) return;
|
|
41
|
+
const k = 1 - cov;
|
|
42
|
+
const s = shade * cov;
|
|
43
|
+
this.r[i] = (this.r[i] ?? 0) * k + col[0] * s;
|
|
44
|
+
this.g[i] = (this.g[i] ?? 0) * k + col[1] * s;
|
|
45
|
+
this.b[i] = (this.b[i] ?? 0) * k + col[2] * s;
|
|
46
|
+
this.a[i] = (this.a[i] ?? 0) * k + cov;
|
|
47
|
+
this.c.height[i] = (this.c.height[i] ?? 0) * k + h * cov;
|
|
48
|
+
this.c.rough[i] = (this.c.rough[i] ?? 0.8) * k + rough * cov;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Pixel bounds of a metre-space box, clipped to the canvas. */
|
|
52
|
+
private span(x0: number, y0: number, x1: number, y1: number): readonly number[] {
|
|
53
|
+
const { w, h } = this.c;
|
|
54
|
+
const p = this.px;
|
|
55
|
+
return [
|
|
56
|
+
Math.max(0, Math.floor(x0 / p) - 1),
|
|
57
|
+
Math.max(0, Math.floor(y0 / p) - 1),
|
|
58
|
+
Math.min(w - 1, Math.ceil(x1 / p) + 1),
|
|
59
|
+
Math.min(h - 1, Math.ceil(y1 / p) + 1),
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** A tapered stem / twig / rachis from (ax, ay) width w0 to (bx, by) width w1, metres. */
|
|
64
|
+
stem(ax: number, ay: number, bx: number, by: number, w0: number, w1: number, col: Rgb): void {
|
|
65
|
+
const m = Math.max(w0, w1);
|
|
66
|
+
const [sx0 = 0, sy0 = 0, sx1 = 0, sy1 = 0] = this.span(
|
|
67
|
+
Math.min(ax, bx) - m,
|
|
68
|
+
Math.min(ay, by) - m,
|
|
69
|
+
Math.max(ax, bx) + m,
|
|
70
|
+
Math.max(ay, by) + m,
|
|
71
|
+
);
|
|
72
|
+
const lx = bx - ax;
|
|
73
|
+
const ly = by - ay;
|
|
74
|
+
const l2 = lx * lx + ly * ly || 1;
|
|
75
|
+
for (let y = sy0; y <= sy1; y++) {
|
|
76
|
+
const ym = (y + 0.5) * this.px;
|
|
77
|
+
for (let x = sx0; x <= sx1; x++) {
|
|
78
|
+
const xm = (x + 0.5) * this.px;
|
|
79
|
+
const t = clamp01(((xm - ax) * lx + (ym - ay) * ly) / l2);
|
|
80
|
+
const half = (w0 + (w1 - w0) * t) / 2;
|
|
81
|
+
const d = segment(xm, ym, ax, ay, bx, by);
|
|
82
|
+
const cov = clamp01(0.5 - (d - half) / this.px);
|
|
83
|
+
const round = half > 0 ? Math.sqrt(Math.max(0, 1 - (d / half) ** 2)) : 0;
|
|
84
|
+
this.put(y * this.c.w + x, cov, col, 1, 0.0015 + half * 0.4 * round, 0.85);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A leaf from its base (bx, by) along `angle` (radians, 0 = +V, positive = toward +U), `len`
|
|
91
|
+
* long and `wid` wide at its widest. `serrate` adds teeth; `lift` raises it over earlier layers.
|
|
92
|
+
*/
|
|
93
|
+
leaf(
|
|
94
|
+
shape: LeafShape,
|
|
95
|
+
bx: number,
|
|
96
|
+
by: number,
|
|
97
|
+
angle: number,
|
|
98
|
+
len: number,
|
|
99
|
+
wid: number,
|
|
100
|
+
col: Rgb,
|
|
101
|
+
serrate = 0,
|
|
102
|
+
lift = 0,
|
|
103
|
+
): void {
|
|
104
|
+
const ux = Math.sin(angle);
|
|
105
|
+
const uy = Math.cos(angle);
|
|
106
|
+
// Bounds: the base-to-tip segment padded by the half-width (palmate: the whole blade disc).
|
|
107
|
+
const tx = bx + ux * len;
|
|
108
|
+
const ty = by + uy * len;
|
|
109
|
+
const pad = shape === 'palmate' ? len : wid;
|
|
110
|
+
const [sx0 = 0, sy0 = 0, sx1 = 0, sy1 = 0] = this.span(
|
|
111
|
+
Math.min(bx, tx) - pad,
|
|
112
|
+
Math.min(by, ty) - pad,
|
|
113
|
+
Math.max(bx, tx) + pad,
|
|
114
|
+
Math.max(by, ty) + pad,
|
|
115
|
+
);
|
|
116
|
+
for (let y = sy0; y <= sy1; y++) {
|
|
117
|
+
const ym = (y + 0.5) * this.px - by;
|
|
118
|
+
for (let x = sx0; x <= sx1; x++) {
|
|
119
|
+
const xm = (x + 0.5) * this.px - bx;
|
|
120
|
+
const along = xm * ux + ym * uy;
|
|
121
|
+
const across = xm * uy - ym * ux;
|
|
122
|
+
let sd: number;
|
|
123
|
+
let rib: number;
|
|
124
|
+
if (shape === 'palmate') {
|
|
125
|
+
// Five pointed lobes around the petiole tip; notch at the stem side.
|
|
126
|
+
const rr = Math.hypot(along - len * 0.45, across);
|
|
127
|
+
const th = Math.atan2(across, along - len * 0.45);
|
|
128
|
+
const lobe = 1 - Math.abs(Math.sin(2.5 * th)) ** 0.6;
|
|
129
|
+
const edge =
|
|
130
|
+
len * 0.5 * (0.35 + 0.65 * lobe) * (1 + serrate * Math.abs(Math.sin(18 * th)));
|
|
131
|
+
sd = rr - edge;
|
|
132
|
+
const axis = Math.round(th / LOBE) * LOBE;
|
|
133
|
+
rib = rr * Math.abs(Math.sin(th - axis));
|
|
134
|
+
} else {
|
|
135
|
+
const t = along / len;
|
|
136
|
+
if (t < 0 || t > 1) {
|
|
137
|
+
sd = Math.max(-along, along - len) + Math.abs(across) * 0.5;
|
|
138
|
+
} else {
|
|
139
|
+
const prof =
|
|
140
|
+
shape === 'ovate'
|
|
141
|
+
? Math.sin(Math.PI * t ** 0.8) ** 0.9
|
|
142
|
+
: shape === 'lance'
|
|
143
|
+
? Math.sin(Math.PI * t ** 0.6)
|
|
144
|
+
: shape === 'blade'
|
|
145
|
+
? (1 - t) ** 0.4
|
|
146
|
+
: 1 - 0.6 * t;
|
|
147
|
+
const teeth = 1 - serrate * Math.abs(Math.sin(t * 40));
|
|
148
|
+
sd = Math.abs(across) - (wid / 2) * prof * teeth;
|
|
149
|
+
}
|
|
150
|
+
rib = Math.abs(across);
|
|
151
|
+
}
|
|
152
|
+
const cov = clamp01(0.5 - sd / this.px);
|
|
153
|
+
if (cov <= 0) continue;
|
|
154
|
+
const inside = clamp01(-sd / (wid * 0.5 + 1e-6));
|
|
155
|
+
const vein = 1 - smooth(0, this.px * 1.5, rib);
|
|
156
|
+
const shade = 0.85 + 0.15 * inside;
|
|
157
|
+
this.put(
|
|
158
|
+
y * this.c.w + x,
|
|
159
|
+
cov,
|
|
160
|
+
col,
|
|
161
|
+
shade,
|
|
162
|
+
0.002 + lift + 0.0015 * inside + 0.0006 * vein,
|
|
163
|
+
0.6,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Write the layers to the canvas: sRGB RGBA with bled colour, occlusion toward the twig. */
|
|
170
|
+
finish(axisX = 0.5, reach = 0.35): void {
|
|
171
|
+
const { w, h } = this.c;
|
|
172
|
+
const n = w * h;
|
|
173
|
+
const sa = total(this.a);
|
|
174
|
+
const mean: Rgb =
|
|
175
|
+
sa > 0 ? [total(this.r) / sa, total(this.g) / sa, total(this.b) / sa] : [0.1, 0.2, 0.05];
|
|
176
|
+
const out = new Uint8Array(n * 4);
|
|
177
|
+
const enc = (v: number): number => SRGB[Math.round(clamp01(v) * 4095)] ?? 0;
|
|
178
|
+
for (let y = 0; y < h; y++) {
|
|
179
|
+
for (let x = 0; x < w; x++) {
|
|
180
|
+
const i = y * w + x;
|
|
181
|
+
const a = this.a[i] ?? 0;
|
|
182
|
+
// Un-premultiply; transparent texels take the mean leaf colour (mip-safe bleed).
|
|
183
|
+
const k = a > 0.02 ? 1 / a : 0;
|
|
184
|
+
const mix = clamp01(a * 4);
|
|
185
|
+
out[i * 4] = enc((this.r[i] ?? 0) * k * mix + mean[0] * (1 - mix));
|
|
186
|
+
out[i * 4 + 1] = enc((this.g[i] ?? 0) * k * mix + mean[1] * (1 - mix));
|
|
187
|
+
out[i * 4 + 2] = enc((this.b[i] ?? 0) * k * mix + mean[2] * (1 - mix));
|
|
188
|
+
out[i * 4 + 3] = Math.round(clamp01(a) * 255);
|
|
189
|
+
const d = Math.abs((x + 0.5) * this.px - axisX);
|
|
190
|
+
this.c.occl[i] = 0.55 + 0.45 * smooth(0, reach, d + (y + 0.5) * this.px * 0.4);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
this.c.rgba = out;
|
|
194
|
+
this.c.metal.fill(0);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Foliage cell dispatch: FoliageCellId → painter. Each paints colour + alpha mask (cutout leaf
|
|
2
|
+
// cards), anchor at the cell's bottom-centre, growth toward +V.
|
|
3
|
+
|
|
4
|
+
import type { FoliageCellId } from '../trim-sheet.ts';
|
|
5
|
+
import type { Canvas } from './canvas.ts';
|
|
6
|
+
import { paintBirch, paintBroadleaf, paintMaple, paintMapleAutumn } from './foliage-broad.ts';
|
|
7
|
+
import { paintAcacia, paintPalm, paintWillow } from './foliage-compound.ts';
|
|
8
|
+
import { paintNeedles, paintReeds, paintSpruce, paintSpruceSnow } from './foliage-conifer.ts';
|
|
9
|
+
|
|
10
|
+
export const FOLIAGE_PAINTERS: Readonly<Record<FoliageCellId, (c: Canvas, seed: number) => void>> =
|
|
11
|
+
{
|
|
12
|
+
foliage_broadleaf: paintBroadleaf,
|
|
13
|
+
foliage_maple: paintMaple,
|
|
14
|
+
foliage_maple_autumn: paintMapleAutumn,
|
|
15
|
+
foliage_birch: paintBirch,
|
|
16
|
+
foliage_acacia: paintAcacia,
|
|
17
|
+
foliage_palm: paintPalm,
|
|
18
|
+
foliage_spruce: paintSpruce,
|
|
19
|
+
foliage_spruce_snow: paintSpruceSnow,
|
|
20
|
+
foliage_willow: paintWillow,
|
|
21
|
+
foliage_reeds: paintReeds,
|
|
22
|
+
foliage_needles: paintNeedles,
|
|
23
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Paint a strip from a CC0 texture set: decode the cached PNGs, box-filter them onto the strip's
|
|
2
|
+
// texel grid at true scale (the set's `metres`, snapped so a whole number of repeats spans the
|
|
3
|
+
// strip's U length — it still tiles), V flipped so the image's top is world up. Albedo stays sRGB
|
|
4
|
+
// colour (tint with NATURAL); the OpenGL normal's green is flipped to the sheet's +Y = −V; `arm`
|
|
5
|
+
// fills occlusion / roughness / metalness, else the recipe's constants.
|
|
6
|
+
|
|
7
|
+
import type { TextureFile, TextureSource } from '../texture-source.ts';
|
|
8
|
+
import { textureBytes } from '../texture-source.ts';
|
|
9
|
+
import type { Canvas } from './canvas.ts';
|
|
10
|
+
import { decodePng, type Rgba } from './decode-png.ts';
|
|
11
|
+
import type { StripRecipe } from './types.ts';
|
|
12
|
+
|
|
13
|
+
const decoded = new Map<string, Rgba>();
|
|
14
|
+
function image(f: TextureFile, where: string): Rgba {
|
|
15
|
+
const hit = decoded.get(f.sha256);
|
|
16
|
+
if (hit) return hit;
|
|
17
|
+
const img = decodePng(textureBytes(f, where));
|
|
18
|
+
decoded.set(f.sha256, img);
|
|
19
|
+
return img;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Average of `img` over the canvas texel footprint, per channel, into out[0..3]. */
|
|
23
|
+
function resample(
|
|
24
|
+
c: Canvas,
|
|
25
|
+
t: TextureSource,
|
|
26
|
+
img: Rgba,
|
|
27
|
+
fn: (i: number, px: Float32Array) => void,
|
|
28
|
+
) {
|
|
29
|
+
const reps = Math.max(1, Math.round(c.metresU / (t.metres[0] ?? 1)));
|
|
30
|
+
const su = c.metresU / reps;
|
|
31
|
+
const sv = t.metres[1] ?? 1;
|
|
32
|
+
const W = img.width;
|
|
33
|
+
const H = img.height;
|
|
34
|
+
const px = new Float32Array(4);
|
|
35
|
+
for (let y = 0; y < c.h; y++) {
|
|
36
|
+
const v0 = ((y * c.metresV) / c.h / sv) * H;
|
|
37
|
+
const v1 = (((y + 1) * c.metresV) / c.h / sv) * H;
|
|
38
|
+
for (let x = 0; x < c.w; x++) {
|
|
39
|
+
const u0 = ((x * c.metresU) / c.w / su) * W;
|
|
40
|
+
const u1 = (((x + 1) * c.metresU) / c.w / su) * W;
|
|
41
|
+
px.fill(0);
|
|
42
|
+
let n = 0;
|
|
43
|
+
for (let sy = Math.floor(v0); sy < Math.max(Math.floor(v0) + 1, Math.ceil(v1)); sy++) {
|
|
44
|
+
const row = H - 1 - (((sy % H) + H) % H);
|
|
45
|
+
for (let sx = Math.floor(u0); sx < Math.max(Math.floor(u0) + 1, Math.ceil(u1)); sx++) {
|
|
46
|
+
const o = (row * W + (((sx % W) + W) % W)) * 4;
|
|
47
|
+
for (let k = 0; k < 4; k++) px[k] = (px[k] ?? 0) + (img.data[o + k] ?? 0);
|
|
48
|
+
n++;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (let k = 0; k < 4; k++) px[k] = (px[k] ?? 0) / n;
|
|
52
|
+
fn(y * c.w + x, px);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function paintImageStrip(c: Canvas, r: StripRecipe, t: TextureSource, where: string): void {
|
|
58
|
+
const albedo = new Uint8Array(c.w * c.h * 4);
|
|
59
|
+
resample(c, t, image(t.maps.albedo, where), (i, p) => {
|
|
60
|
+
albedo.set([p[0] ?? 0, p[1] ?? 0, p[2] ?? 0, 255], i * 4);
|
|
61
|
+
});
|
|
62
|
+
c.rgba = albedo;
|
|
63
|
+
c.height.fill(0);
|
|
64
|
+
if (t.maps.normal) {
|
|
65
|
+
const nrm = new Uint8Array(c.w * c.h * 4);
|
|
66
|
+
resample(c, t, image(t.maps.normal, where), (i, p) => {
|
|
67
|
+
const x = (p[0] ?? 128) / 127.5 - 1;
|
|
68
|
+
const y = -((p[1] ?? 128) / 127.5 - 1);
|
|
69
|
+
const z = (p[2] ?? 255) / 127.5 - 1;
|
|
70
|
+
const l = Math.hypot(x, y, z) || 1;
|
|
71
|
+
nrm.set(
|
|
72
|
+
[(x / l) * 127.5 + 127.5, (y / l) * 127.5 + 127.5, (z / l) * 127.5 + 127.5, 255],
|
|
73
|
+
i * 4,
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
c.normalRgba = nrm;
|
|
77
|
+
}
|
|
78
|
+
if (t.maps.arm) {
|
|
79
|
+
resample(c, t, image(t.maps.arm, where), (i, p) => {
|
|
80
|
+
c.occl[i] = (p[0] ?? 255) / 255;
|
|
81
|
+
c.rough[i] = (p[1] ?? 204) / 255;
|
|
82
|
+
c.metal[i] = (p[2] ?? 0) / 255;
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
c.rough.fill(r.roughness);
|
|
86
|
+
c.metal.fill(r.metalness ?? 0);
|
|
87
|
+
}
|
|
88
|
+
}
|