@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,63 @@
|
|
|
1
|
+
// Head landmarks from params: where the cranium, eyes, brow, nose base, mouth, chin, jaw hinge and
|
|
2
|
+
// jaw angle sit, in head units (x front, y left, z up; z 0 = chin of an undropped jaw, x 0 ≈ ear
|
|
3
|
+
// canal). Every edit list reads this one table, so moving the eye line moves lids, brow and nose.
|
|
4
|
+
|
|
5
|
+
import type { Vec3 } from '../core/index.ts';
|
|
6
|
+
import type { HeadParams } from './head-params.ts';
|
|
7
|
+
|
|
8
|
+
export interface HeadLayout {
|
|
9
|
+
/** Cranium ellipsoid centre and radii. */
|
|
10
|
+
readonly cranium: { readonly at: Vec3; readonly r: Vec3 };
|
|
11
|
+
/** Forehead front at the eye line. */
|
|
12
|
+
readonly faceX: number;
|
|
13
|
+
/** Left eye centre (mirror for the right) and eyeball radius. */
|
|
14
|
+
readonly eye: Vec3;
|
|
15
|
+
readonly eyeR: number;
|
|
16
|
+
readonly browZ: number;
|
|
17
|
+
readonly noseZ: number;
|
|
18
|
+
readonly mouthZ: number;
|
|
19
|
+
/** Front of the lips. */
|
|
20
|
+
readonly mouthX: number;
|
|
21
|
+
readonly chin: Vec3;
|
|
22
|
+
/** Jaw hinge (x, z) — the jaw part's pivot. */
|
|
23
|
+
readonly hinge: readonly [number, number];
|
|
24
|
+
/** Left jaw angle (gonion). */
|
|
25
|
+
readonly angle: Vec3;
|
|
26
|
+
/** Top of the head, units. */
|
|
27
|
+
readonly crown: number;
|
|
28
|
+
/** Bottom of the bust (the asset's z = 0), units. */
|
|
29
|
+
readonly base: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function headLayout(p: HeadParams): HeadLayout {
|
|
33
|
+
const L = p.eyes.line;
|
|
34
|
+
const crH = p.cranium.height;
|
|
35
|
+
const rx = p.cranium.depth / 2;
|
|
36
|
+
const ry = p.cranium.width / 2;
|
|
37
|
+
const rz = crH * 0.8;
|
|
38
|
+
const cx = -0.02 - p.jaw.muzzle * 0.4;
|
|
39
|
+
const cz = L + crH * 0.2;
|
|
40
|
+
const faceX = cx + rx * 0.97;
|
|
41
|
+
const jut = p.jaw.muzzle;
|
|
42
|
+
const eyeR = p.eyes.radius;
|
|
43
|
+
const eye: Vec3 = [faceX - eyeR - p.brow.overhang, p.eyes.spacing, L];
|
|
44
|
+
const noseZ = L - p.nose.length;
|
|
45
|
+
const mouthZ = noseZ - 0.1 - p.jaw.drop * 0.3;
|
|
46
|
+
const chinZ = -p.jaw.drop;
|
|
47
|
+
const mouthX = faceX - 0.01 + jut + p.jaw.underbite * 0.3;
|
|
48
|
+
return {
|
|
49
|
+
cranium: { at: [cx, 0, cz], r: [rx, ry, rz] },
|
|
50
|
+
faceX,
|
|
51
|
+
eye,
|
|
52
|
+
eyeR,
|
|
53
|
+
browZ: L + eyeR * 0.9 + p.brow.ridge * 0.6,
|
|
54
|
+
noseZ,
|
|
55
|
+
mouthZ,
|
|
56
|
+
mouthX,
|
|
57
|
+
chin: [faceX - 0.07 + jut + p.jaw.underbite, 0, chinZ + 0.07 * p.jaw.chin],
|
|
58
|
+
hinge: [cx + 0.04, L - 0.09],
|
|
59
|
+
angle: [cx - 0.02, p.jaw.width, 0.15 - p.jaw.drop * 0.6],
|
|
60
|
+
crown: cz + rz,
|
|
61
|
+
base: -0.85 - p.jaw.drop * 0.5,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// Head edit lists → meshes. One skull field is split by the jaw cutter into `head` (skull − cut,
|
|
2
|
+
// smooth-unioned with the ears) and `jaw` (skull ∩ cut, with the tusks), so the two parts share
|
|
3
|
+
// the exact same outer surface; their cut faces are painted mouth-dark for when the jaw opens.
|
|
4
|
+
// Eyeballs are their own spheres: sclera, iris, pupil by angle from the +X gaze.
|
|
5
|
+
|
|
6
|
+
import type { Mesh, Vec3 } from '../core/index.ts';
|
|
7
|
+
import { type Field, type SculptEdit, sculptField, sd, sdf } from '../primitives/index.ts';
|
|
8
|
+
import type { HeadLayout } from './head-layout.ts';
|
|
9
|
+
import type { HeadParams } from './head-params.ts';
|
|
10
|
+
import { creaseNormals, sharpenRim } from './head-rim.ts';
|
|
11
|
+
import { toned } from './head-tone.ts';
|
|
12
|
+
|
|
13
|
+
/** Cavity probe of the painted tone, head units: about one fold. */
|
|
14
|
+
const REACH = 0.05;
|
|
15
|
+
/** Default plinth colour (dark slate). */
|
|
16
|
+
export const PLINTH: Vec3 = [0.16, 0.15, 0.15];
|
|
17
|
+
|
|
18
|
+
/** The jaw region (head units): an ellipsoid around chin and lower lip, cut flat at the mouth
|
|
19
|
+
* line. Its skin seam runs along the lips, down the marionette lines and under the chin — creases
|
|
20
|
+
* a face already has — instead of across the cheek. */
|
|
21
|
+
export function jawRegion(
|
|
22
|
+
p: HeadParams,
|
|
23
|
+
l: HeadLayout,
|
|
24
|
+
): { field: Field; centre: Vec3; radii: Vec3 } {
|
|
25
|
+
const chin = l.chin;
|
|
26
|
+
const reach = Math.max(p.mouth.width * 1.08, (p.tusks?.spread ?? 0) + p.mouth.width * 0.5);
|
|
27
|
+
const centre: Vec3 = [l.mouthX - 0.15, 0, l.mouthZ];
|
|
28
|
+
const radii: Vec3 = [Math.max(0.27, l.mouthX - chin[0] + 0.2), reach, l.mouthZ - chin[2] + 0.1];
|
|
29
|
+
const e = sd.ellipsoid(centre, radii);
|
|
30
|
+
return { field: (q) => Math.max(q[2] - l.mouthZ, e(q)), centre, radii };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const unionBounds = (a: readonly [Vec3, Vec3], b?: readonly [Vec3, Vec3]): [Vec3, Vec3] =>
|
|
34
|
+
b
|
|
35
|
+
? [
|
|
36
|
+
[Math.min(a[0][0], b[0][0]), Math.min(a[0][1], b[0][1]), Math.min(a[0][2], b[0][2])],
|
|
37
|
+
[Math.max(a[1][0], b[1][0]), Math.max(a[1][1], b[1][1]), Math.max(a[1][2], b[1][2])],
|
|
38
|
+
]
|
|
39
|
+
: [a[0], a[1]];
|
|
40
|
+
|
|
41
|
+
/** The jaw region as a sculpt group (ellipsoid ∩ below the mouth line), grown by `grow`. */
|
|
42
|
+
function regionGroup(l: HeadLayout, r: { centre: Vec3; radii: Vec3 }, grow: number): SculptEdit {
|
|
43
|
+
const cx = r.centre[0];
|
|
44
|
+
const [rx, ry, rz] = r.radii;
|
|
45
|
+
return {
|
|
46
|
+
group: [
|
|
47
|
+
{ shape: 'ellipsoid', at: r.centre, size: [rx + grow, ry + grow, rz + grow] },
|
|
48
|
+
{
|
|
49
|
+
shape: 'roundBox',
|
|
50
|
+
at: [cx, 0, l.mouthZ + grow - 1],
|
|
51
|
+
size: [4, 4, 2],
|
|
52
|
+
op: 'intersect',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
mirror: false,
|
|
56
|
+
blend: 0,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface HeadEditSet {
|
|
61
|
+
readonly skull: readonly SculptEdit[];
|
|
62
|
+
readonly ears: readonly SculptEdit[];
|
|
63
|
+
readonly tusks: readonly SculptEdit[];
|
|
64
|
+
readonly bust: readonly SculptEdit[];
|
|
65
|
+
/** The bust-cut region (≈ 0 on the cut faces), painted as bare plinth material. */
|
|
66
|
+
readonly bustCut?: Field;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** `head` and `jaw` meshes in head units at cell size `res` (units). Each is ONE sculpt (skull +
|
|
70
|
+
* ears − region, skull ∩ region + tusks), so the sculpt's range skipping covers every edit. */
|
|
71
|
+
export function headAndJaw(
|
|
72
|
+
p: HeadParams,
|
|
73
|
+
l: HeadLayout,
|
|
74
|
+
set: HeadEditSet,
|
|
75
|
+
res: number,
|
|
76
|
+
): { head: Mesh; jaw: Mesh } {
|
|
77
|
+
const opts = { color: p.colors.skin, mirror: 'y' as const };
|
|
78
|
+
const region = jawRegion(p, l);
|
|
79
|
+
const cut = region.field;
|
|
80
|
+
// The jaw reaches a little past the cut and sits a hair proud of the skin, so it laps over the
|
|
81
|
+
// head like a lip: no gap opens along the seam where meshing leaves the two rims a cell apart.
|
|
82
|
+
const lap = res * 0.75;
|
|
83
|
+
const proud = res * 0.03;
|
|
84
|
+
const ears: SculptEdit[] = set.ears.length > 0 ? [{ group: set.ears, blend: 0.02 }] : [];
|
|
85
|
+
const tusks: SculptEdit[] = set.tusks.length > 0 ? [{ group: set.tusks, blend: 0.01 }] : [];
|
|
86
|
+
const h = sculptField(
|
|
87
|
+
[...set.skull, ...ears, { ...regionGroup(l, region, 0), op: 'subtract' }],
|
|
88
|
+
opts,
|
|
89
|
+
);
|
|
90
|
+
const jRaw = sculptField(
|
|
91
|
+
[...set.skull, { ...regionGroup(l, region, lap), op: 'intersect' }, ...tusks],
|
|
92
|
+
opts,
|
|
93
|
+
);
|
|
94
|
+
const jField: Field = (q) => jRaw.field(q) - proud;
|
|
95
|
+
const mouth = p.colors.mouth;
|
|
96
|
+
// Only the skin-only skull is needed near the cut (paint + rim snapping): evaluated lazily.
|
|
97
|
+
const skull = sculptField(set.skull, opts).field;
|
|
98
|
+
// The painted tone reads the uncut surface (skin + ears, skin + tusks): the cut faces' normals
|
|
99
|
+
// would light the jaw rim up as a pale outline.
|
|
100
|
+
const extra = (e: readonly SculptEdit[]): Field =>
|
|
101
|
+
e.length > 0
|
|
102
|
+
? (
|
|
103
|
+
(f: Field): Field =>
|
|
104
|
+
(q) =>
|
|
105
|
+
Math.min(skull(q), f(q))
|
|
106
|
+
)(sculptField(e, opts).field)
|
|
107
|
+
: skull;
|
|
108
|
+
const headTone = extra(set.ears);
|
|
109
|
+
const jawTone = extra(set.tusks);
|
|
110
|
+
const deep = -Math.max(res * 4, 0.03);
|
|
111
|
+
const paint =
|
|
112
|
+
(colorAt: (q: Vec3) => Vec3) =>
|
|
113
|
+
(q: Vec3): Vec3 =>
|
|
114
|
+
Math.abs(cut(q)) < res * 1.5 && skull(q) < deep ? mouth : colorAt(q);
|
|
115
|
+
const [ec, er] = [region.centre, region.radii];
|
|
116
|
+
const jb: [Vec3, Vec3] = [
|
|
117
|
+
[ec[0] - er[0] - lap, -er[1] - lap, ec[2] - er[2] - lap],
|
|
118
|
+
[ec[0] + er[0] + lap, er[1] + lap, ec[2] + lap + 0.01],
|
|
119
|
+
];
|
|
120
|
+
const tb = set.tusks.length > 0 ? sculptField(set.tusks, opts).bounds : undefined;
|
|
121
|
+
const head = sdf({
|
|
122
|
+
field: h.field,
|
|
123
|
+
bounds: h.bounds,
|
|
124
|
+
resolution: res,
|
|
125
|
+
color: toned(headTone, paint(h.colorAt), { reach: REACH }),
|
|
126
|
+
symmetry: 'y',
|
|
127
|
+
});
|
|
128
|
+
const jaw = sdf({
|
|
129
|
+
field: jField,
|
|
130
|
+
bounds: unionBounds(jb, tb),
|
|
131
|
+
resolution: res,
|
|
132
|
+
color: toned(jawTone, paint(jRaw.colorAt), { reach: REACH }),
|
|
133
|
+
symmetry: 'y',
|
|
134
|
+
});
|
|
135
|
+
// Coarse cells: snapping folds triangles over, and nobody sees a seam at that distance.
|
|
136
|
+
if (res > 0.03) return { head: creaseNormals(head), jaw: creaseNormals(jaw) };
|
|
137
|
+
const jawSkull: Field = (q) => skull(q) - proud;
|
|
138
|
+
return {
|
|
139
|
+
// The head's rim lies under the jaw's lap, so only the jaw rim (the visible one) is snapped.
|
|
140
|
+
head: creaseNormals(head),
|
|
141
|
+
jaw: creaseNormals(sharpenRim(jaw, jawSkull, (q) => cut(q) - lap, jField, res)),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** A surface point lies on a cut face: on the cut boundary, facing out of the cut region. */
|
|
146
|
+
function onCut(cut: Field, q: Vec3, n: Vec3, res: number): boolean {
|
|
147
|
+
if (Math.abs(cut(q)) > res * 0.8) return false;
|
|
148
|
+
const e = res * 0.5;
|
|
149
|
+
const g: Vec3 = [
|
|
150
|
+
cut([q[0] + e, q[1], q[2]]) - cut([q[0] - e, q[1], q[2]]),
|
|
151
|
+
cut([q[0], q[1] + e, q[2]]) - cut([q[0], q[1] - e, q[2]]),
|
|
152
|
+
cut([q[0], q[1], q[2] + e]) - cut([q[0], q[1], q[2] - e]),
|
|
153
|
+
];
|
|
154
|
+
const l = Math.hypot(g[0], g[1], g[2]) || 1;
|
|
155
|
+
return (g[0] * n[0] + g[1] * n[1] + g[2] * n[2]) / l > 0.85;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function bustMesh(p: HeadParams, set: HeadEditSet, res: number): Mesh {
|
|
159
|
+
const s = sculptField(set.bust, { color: p.colors.skin, mirror: 'y' });
|
|
160
|
+
const tone = toned(s.field, s.colorAt, { reach: REACH * 1.4 });
|
|
161
|
+
const cut = set.bustCut;
|
|
162
|
+
const base = p.colors.plinth ?? PLINTH;
|
|
163
|
+
const face: Vec3 = [base[0] * 1.3, base[1] * 1.3, base[2] * 1.3];
|
|
164
|
+
return sdf({
|
|
165
|
+
field: s.field,
|
|
166
|
+
bounds: s.bounds,
|
|
167
|
+
resolution: res,
|
|
168
|
+
// Cut faces read as the bare material of a sculpted bust, not as sliced layers.
|
|
169
|
+
color: cut ? (q, n) => (onCut(cut, q, n, res) ? face : tone(q)) : tone,
|
|
170
|
+
symmetry: 'y',
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Eyeball of radius `r` centred on the origin, gazing +X. */
|
|
175
|
+
export function eyeMesh(p: HeadParams, r: number, res: number): Mesh {
|
|
176
|
+
const c = p.colors;
|
|
177
|
+
const iris = Math.cos(0.62);
|
|
178
|
+
const limbus = Math.cos(0.7);
|
|
179
|
+
const pupil = Math.cos(0.3);
|
|
180
|
+
return sdf({
|
|
181
|
+
field: (q) => Math.hypot(q[0], q[1], q[2]) - r,
|
|
182
|
+
bounds: [
|
|
183
|
+
[-r * 1.3, -r * 1.3, -r * 1.3],
|
|
184
|
+
[r * 1.3, r * 1.3, r * 1.3],
|
|
185
|
+
],
|
|
186
|
+
resolution: res,
|
|
187
|
+
color: (q) => {
|
|
188
|
+
const cos = q[0] / (Math.hypot(q[0], q[1], q[2]) || 1);
|
|
189
|
+
if (cos > pupil) return c.pupil;
|
|
190
|
+
if (cos > iris) return c.iris;
|
|
191
|
+
if (cos > limbus) return [c.iris[0] * 0.45, c.iris[1] * 0.45, c.iris[2] * 0.45];
|
|
192
|
+
return c.sclera;
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Lips and mouth as edit lists, part of the skull field: upper and lower lip pads (the lower one
|
|
2
|
+
// pushed forward by the underbite), then the opening — a thin scowl line when `grin` is low, or a
|
|
3
|
+
// crescent cut (a wide smile, corners lifted) lined with small pointed teeth along the dental arch
|
|
4
|
+
// when it is high. Head units, mirrored across Y.
|
|
5
|
+
|
|
6
|
+
import type { SculptEdit } from '../primitives/index.ts';
|
|
7
|
+
import { alongZ } from './head-geom.ts';
|
|
8
|
+
import type { HeadLayout } from './head-layout.ts';
|
|
9
|
+
import type { HeadParams } from './head-params.ts';
|
|
10
|
+
|
|
11
|
+
export function mouthEdits(p: HeadParams, l: HeadLayout, teeth = true): SculptEdit[] {
|
|
12
|
+
const c = p.colors;
|
|
13
|
+
const lipT = p.mouth.lips;
|
|
14
|
+
const mw = p.mouth.width;
|
|
15
|
+
const g = p.mouth.grin;
|
|
16
|
+
const out: SculptEdit[] = [
|
|
17
|
+
{
|
|
18
|
+
shape: 'ellipsoid',
|
|
19
|
+
at: [l.mouthX - 0.01, 0, l.mouthZ + lipT * 0.7],
|
|
20
|
+
size: [0.032, mw, lipT],
|
|
21
|
+
color: c.lips,
|
|
22
|
+
blend: 0.03,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
shape: 'ellipsoid',
|
|
26
|
+
at: [l.mouthX - 0.015 + p.jaw.underbite * 0.7, 0, l.mouthZ - lipT * 0.8],
|
|
27
|
+
size: [0.034 + p.jaw.underbite * 0.3, mw * 0.85, lipT * (1.1 + p.jaw.underbite * 4)],
|
|
28
|
+
color: c.lips,
|
|
29
|
+
blend: 0.03,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
// Far LODs carve a grin as a plain wide line: a crescent that thin folds over at coarse cells.
|
|
33
|
+
if (g < 0.4 || !teeth) {
|
|
34
|
+
out.push({
|
|
35
|
+
shape: 'capsule',
|
|
36
|
+
at: [l.mouthX + 0.05, 0, l.mouthZ],
|
|
37
|
+
to: [l.mouthX - 0.03 - 0.02 * g, mw * 0.98, l.mouthZ + 0.012 + g * 0.05 - p.brow.scowl * 0.4],
|
|
38
|
+
size: 0.006 + g * 0.022,
|
|
39
|
+
op: 'subtract',
|
|
40
|
+
blend: 0.012,
|
|
41
|
+
color: c.mouth,
|
|
42
|
+
});
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
// A grin: the part of a wide ellipsoid below a higher one — a crescent whose horns rise.
|
|
46
|
+
const h = 0.035 + g * 0.05;
|
|
47
|
+
const z0 = l.mouthZ + h * 0.45;
|
|
48
|
+
const back = mw * 0.55;
|
|
49
|
+
out.push({
|
|
50
|
+
group: [
|
|
51
|
+
{ shape: 'ellipsoid', at: [l.mouthX - back, 0, z0], size: [back + 0.06, mw, h] },
|
|
52
|
+
{
|
|
53
|
+
shape: 'ellipsoid',
|
|
54
|
+
at: [l.mouthX - back + 0.02, 0, z0 + h * 0.7],
|
|
55
|
+
size: [back + 0.12, mw * 1.1, h * 0.9],
|
|
56
|
+
op: 'subtract',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
op: 'subtract',
|
|
60
|
+
blend: 0.012,
|
|
61
|
+
color: c.mouth,
|
|
62
|
+
});
|
|
63
|
+
// A tongue fills the floor of the open mouth (so no flat jaw cut shows through the grin).
|
|
64
|
+
if (teeth)
|
|
65
|
+
out.push({
|
|
66
|
+
shape: 'ellipsoid',
|
|
67
|
+
at: [l.mouthX - back * 0.9, 0, l.mouthZ - h * 0.4],
|
|
68
|
+
size: [back * 0.9, mw * 0.62, h * 0.45],
|
|
69
|
+
color: [0.42, 0.12, 0.12],
|
|
70
|
+
blend: 0.01,
|
|
71
|
+
});
|
|
72
|
+
// The grin bunches the cheeks up and out beside the mouth corners.
|
|
73
|
+
out.push({
|
|
74
|
+
shape: 'ellipsoid',
|
|
75
|
+
at: [l.mouthX - 0.06, mw * 0.95, z0 + h * 0.6],
|
|
76
|
+
size: [0.05, 0.045, 0.04 + g * 0.01],
|
|
77
|
+
rotate: [-30, 0, 0],
|
|
78
|
+
blend: 0.035,
|
|
79
|
+
});
|
|
80
|
+
// Teeth: pointed little cones along the arch — the upper row hangs, the lower row rises.
|
|
81
|
+
const n = teeth ? 3 : 0;
|
|
82
|
+
for (let i = 0; i < n; i++) {
|
|
83
|
+
const a = (0.25 + i) * 0.36;
|
|
84
|
+
const y = Math.sin(a) * mw * 0.82;
|
|
85
|
+
const x = l.mouthX - 0.03 - (1 - Math.cos(a)) * back * 1.3;
|
|
86
|
+
// Follow the crescent: roots just inside its upper and lower edges, corners higher.
|
|
87
|
+
const lo = z0 - h * Math.sqrt(Math.max(0, 1 - (y / mw) ** 2));
|
|
88
|
+
const hi = z0 + h * 0.7 - h * 0.9 * Math.sqrt(Math.max(0, 1 - (y / (mw * 1.1)) ** 2));
|
|
89
|
+
const len = 0.03 + g * 0.008 - i * 0.004;
|
|
90
|
+
const r = 0.014 + g * 0.004 - i * 0.0015;
|
|
91
|
+
out.push(
|
|
92
|
+
{
|
|
93
|
+
shape: 'cone',
|
|
94
|
+
at: [x, y, hi + 0.008],
|
|
95
|
+
size: [r, 0.003, len],
|
|
96
|
+
rotate: alongZ([0.15, 0, -1]),
|
|
97
|
+
color: c.teeth,
|
|
98
|
+
blend: 0.004,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
shape: 'cone',
|
|
102
|
+
at: [x - 0.006, y * 0.97, lo - 0.008],
|
|
103
|
+
size: [r * 0.85, 0.003, len * 0.6],
|
|
104
|
+
rotate: alongZ([0.1, 0, 1]),
|
|
105
|
+
color: c.teeth,
|
|
106
|
+
blend: 0.004,
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Head kind parameters: the typed knobs of a stylised head, in HEAD UNITS (1 = chin to crown of a
|
|
2
|
+
// human-proportioned skull; the builder scales by `height` metres at the end), plus the guard that
|
|
3
|
+
// turns an unusable set into a T3D_RECIPE_INVALID with the fix.
|
|
4
|
+
|
|
5
|
+
import { Ts23dError, type Vec3 } from '../core/index.ts';
|
|
6
|
+
|
|
7
|
+
export type EarShape = 'round' | 'pointed';
|
|
8
|
+
export type HairStyle = 'none' | 'topknot' | 'mohawk' | 'swept' | 'tufts' | 'braids';
|
|
9
|
+
export type BeardStyle = 'none' | 'short' | 'braided' | 'goatee';
|
|
10
|
+
export type BustStyle = 'pauldrons' | 'hood' | 'tunic' | 'vest';
|
|
11
|
+
|
|
12
|
+
export interface HeadColors {
|
|
13
|
+
readonly skin: Vec3;
|
|
14
|
+
/** Darker skin: lips edge, nostrils, inner ear, wrinkles. */
|
|
15
|
+
readonly shade: Vec3;
|
|
16
|
+
readonly lips: Vec3;
|
|
17
|
+
readonly sclera: Vec3;
|
|
18
|
+
readonly iris: Vec3;
|
|
19
|
+
readonly pupil: Vec3;
|
|
20
|
+
readonly teeth: Vec3;
|
|
21
|
+
readonly mouth: Vec3;
|
|
22
|
+
readonly hair: Vec3;
|
|
23
|
+
readonly cloth: Vec3;
|
|
24
|
+
readonly leather: Vec3;
|
|
25
|
+
readonly metal: Vec3;
|
|
26
|
+
/** Cloak / mantle over the shoulders; default `leather`. */
|
|
27
|
+
readonly cape?: Vec3;
|
|
28
|
+
/** Fur trim on the bust (mantle, hood lining); default `leather`. */
|
|
29
|
+
readonly fur?: Vec3;
|
|
30
|
+
/** War paint / tattoo stripes (orc), or none. */
|
|
31
|
+
readonly paint?: Vec3;
|
|
32
|
+
/** The bust's plinth (stone/wood socle); default a dark slate. */
|
|
33
|
+
readonly plinth?: Vec3;
|
|
34
|
+
/** Scarf / sash cloth (vest style); default `cloth`. */
|
|
35
|
+
readonly scarf?: Vec3;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface HeadParams {
|
|
39
|
+
/** Chin to crown, metres (adult human ≈ 0.23). */
|
|
40
|
+
readonly height: number;
|
|
41
|
+
/** Full width / depth of the brain case; `height` = crown above the eye line. */
|
|
42
|
+
readonly cranium: { readonly width: number; readonly depth: number; readonly height: number };
|
|
43
|
+
/** Ridge radius, how far it juts past the eyes, and the scowl (inner ends lowered). */
|
|
44
|
+
/** Ridge radius, overhang past the eyes, scowl (inner ends lowered), eyebrow hair radius
|
|
45
|
+
* (omit or 0 = no eyebrows). */
|
|
46
|
+
readonly brow: {
|
|
47
|
+
readonly ridge: number;
|
|
48
|
+
readonly overhang: number;
|
|
49
|
+
readonly scowl: number;
|
|
50
|
+
readonly hair?: number;
|
|
51
|
+
};
|
|
52
|
+
readonly cheeks: { readonly size: number; readonly width: number };
|
|
53
|
+
/** Half-width at the jaw angle, chin size, lower-jaw jut past the upper lip, chin drop,
|
|
54
|
+
* muzzle (how far mouth and jaw push forward of the brow: prognathism). */
|
|
55
|
+
readonly jaw: {
|
|
56
|
+
readonly muzzle: number;
|
|
57
|
+
readonly width: number;
|
|
58
|
+
readonly chin: number;
|
|
59
|
+
readonly underbite: number;
|
|
60
|
+
readonly drop: number;
|
|
61
|
+
};
|
|
62
|
+
/** Eye line → nose base, nostril half-width, tip radius, hook (droop), bridge projection. */
|
|
63
|
+
readonly nose: {
|
|
64
|
+
readonly length: number;
|
|
65
|
+
readonly width: number;
|
|
66
|
+
readonly tip: number;
|
|
67
|
+
readonly hook: number;
|
|
68
|
+
readonly bridge: number;
|
|
69
|
+
};
|
|
70
|
+
/** Length (root → tip), width, sweep back (deg), tilt up (deg). */
|
|
71
|
+
readonly ears: {
|
|
72
|
+
readonly shape: EarShape;
|
|
73
|
+
readonly length: number;
|
|
74
|
+
readonly width: number;
|
|
75
|
+
readonly sweep: number;
|
|
76
|
+
readonly lift: number;
|
|
77
|
+
};
|
|
78
|
+
/** Eyeball radius, centre-to-midline, eye line (fraction of height), lid angles (deg). */
|
|
79
|
+
readonly eyes: {
|
|
80
|
+
readonly radius: number;
|
|
81
|
+
readonly spacing: number;
|
|
82
|
+
readonly line: number;
|
|
83
|
+
readonly lid: number;
|
|
84
|
+
readonly lowerLid: number;
|
|
85
|
+
};
|
|
86
|
+
/** Half-width, lip thickness, grin (0 = closed line, 1 = wide toothy smile). */
|
|
87
|
+
readonly mouth: { readonly width: number; readonly lips: number; readonly grin: number };
|
|
88
|
+
readonly tusks: {
|
|
89
|
+
readonly length: number;
|
|
90
|
+
readonly radius: number;
|
|
91
|
+
readonly spread: number;
|
|
92
|
+
readonly flare: number;
|
|
93
|
+
} | null;
|
|
94
|
+
readonly neck: { readonly radius: number };
|
|
95
|
+
/** Shoulder half-span, gear style, muscle mass (0 = bony and narrow … 1 = brute; default 0.5). */
|
|
96
|
+
readonly bust: { readonly shoulders: number; readonly style: BustStyle; readonly mass?: number };
|
|
97
|
+
readonly hair: HairStyle;
|
|
98
|
+
readonly beard: BeardStyle;
|
|
99
|
+
/** Wart / bump count scattered on the skin (seeded). */
|
|
100
|
+
readonly warts: number;
|
|
101
|
+
/** Forehead creases (0 = smooth; a goblin's worried brow has 3). */
|
|
102
|
+
readonly wrinkles?: number;
|
|
103
|
+
readonly colors: HeadColors;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const bad = (cause: string, fix: string): never => {
|
|
107
|
+
throw new Ts23dError('T3D_RECIPE_INVALID', { cause, fix });
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** Every number in a params object, with its path, for the finite/positive sweep. */
|
|
111
|
+
function numbers(o: unknown, path: string, out: Array<[string, number]>): void {
|
|
112
|
+
if (typeof o === 'number') out.push([path, o]);
|
|
113
|
+
else if (o && typeof o === 'object' && !Array.isArray(o))
|
|
114
|
+
for (const [k, v] of Object.entries(o)) numbers(v, path ? `${path}.${k}` : k, out);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const SIGNED = new Set([
|
|
118
|
+
'brow.scowl',
|
|
119
|
+
'jaw.underbite',
|
|
120
|
+
'nose.hook',
|
|
121
|
+
'ears.lift',
|
|
122
|
+
'ears.sweep',
|
|
123
|
+
'nose.bridge',
|
|
124
|
+
]);
|
|
125
|
+
const ZERO_OK = new Set([
|
|
126
|
+
'mouth.grin',
|
|
127
|
+
'warts',
|
|
128
|
+
'jaw.drop',
|
|
129
|
+
'jaw.muzzle',
|
|
130
|
+
'brow.overhang',
|
|
131
|
+
'brow.hair',
|
|
132
|
+
'bust.mass',
|
|
133
|
+
'wrinkles',
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
/** Throws T3D_RECIPE_INVALID (with a fix) when `p` cannot make a head. */
|
|
137
|
+
export function checkHead(p: HeadParams): HeadParams {
|
|
138
|
+
if (!(p.height >= 0.05 && p.height <= 3))
|
|
139
|
+
bad(`head height ${p.height} m`, 'give height in metres, chin to crown: human 0.23, orc ≈ 0.3');
|
|
140
|
+
const flat: Array<[string, number]> = [];
|
|
141
|
+
numbers({ ...p, colors: undefined, height: undefined }, '', flat);
|
|
142
|
+
for (const [k, v] of flat) {
|
|
143
|
+
if (!Number.isFinite(v)) bad(`head param ${k} is ${v}`, 'use finite numbers (head units)');
|
|
144
|
+
if (SIGNED.has(k)) continue;
|
|
145
|
+
if (v < 0 || (v === 0 && !ZERO_OK.has(k)))
|
|
146
|
+
bad(`head param ${k} is ${v}`, `make ${k} > 0 (head units: 1 = chin to crown)`);
|
|
147
|
+
}
|
|
148
|
+
const e = p.eyes;
|
|
149
|
+
if (e.line < 0.3 || e.line > 0.7)
|
|
150
|
+
bad(`eyes.line ${e.line}`, 'keep the eye line between 0.3 and 0.7 of the head height');
|
|
151
|
+
if (e.spacing < e.radius * 1.1)
|
|
152
|
+
bad(
|
|
153
|
+
`eyes.spacing ${e.spacing} < eyes.radius ${e.radius} × 1.1: the eyeballs overlap`,
|
|
154
|
+
'raise eyes.spacing (centre to midline) or shrink eyes.radius',
|
|
155
|
+
);
|
|
156
|
+
if (e.lid <= e.lowerLid * -1 || e.lid > 80)
|
|
157
|
+
bad(`eye lids ${e.lid}°/${e.lowerLid}° leave no opening`, 'use lid in (−lowerLid, 80] degrees');
|
|
158
|
+
if (p.mouth.grin > 1) bad(`mouth.grin ${p.mouth.grin}`, 'grin runs 0 (closed) … 1 (wide)');
|
|
159
|
+
if (p.tusks && p.tusks.length > 0.6)
|
|
160
|
+
bad(`tusks.length ${p.tusks.length}`, 'tusks longer than 0.6 head units read as horns');
|
|
161
|
+
return p;
|
|
162
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Race presets for the head kind: plain `HeadParams` data in head units (1 = chin to crown) —
|
|
2
|
+
// spread one and override (`{ ...orc, tusks: null }` is a tuskless orc). Original stylised designs
|
|
3
|
+
// in the chunky hand-painted fantasy idiom, not copies of any game's characters. Proportions and
|
|
4
|
+
// palettes are documented per recipe in recipes/<race>_head/README.md.
|
|
5
|
+
|
|
6
|
+
import { hex } from '../material/index.ts';
|
|
7
|
+
import type { HeadParams } from './head-params.ts';
|
|
8
|
+
|
|
9
|
+
/** Classical proportions, slightly heroic: eye line at half height, thirds brow/nose/chin. */
|
|
10
|
+
export const human: HeadParams = {
|
|
11
|
+
height: 0.23,
|
|
12
|
+
cranium: { width: 0.66, depth: 0.86, height: 0.5 },
|
|
13
|
+
brow: { ridge: 0.035, overhang: 0.03, scowl: 0.012, hair: 0.018 },
|
|
14
|
+
cheeks: { size: 1, width: 1 },
|
|
15
|
+
jaw: { muzzle: 0, width: 0.22, chin: 1.05, underbite: 0, drop: 0 },
|
|
16
|
+
nose: { length: 0.18, width: 0.06, tip: 0.036, hook: 0, bridge: 0 },
|
|
17
|
+
ears: { shape: 'round', length: 0.27, width: 0.15, sweep: 12, lift: 0 },
|
|
18
|
+
eyes: { radius: 0.052, spacing: 0.135, line: 0.5, lid: 20, lowerLid: 30 },
|
|
19
|
+
mouth: { width: 0.1, lips: 0.018, grin: 0 },
|
|
20
|
+
tusks: null,
|
|
21
|
+
neck: { radius: 0.27 },
|
|
22
|
+
bust: { shoulders: 0.95, style: 'tunic' },
|
|
23
|
+
hair: 'swept',
|
|
24
|
+
beard: 'short',
|
|
25
|
+
warts: 0,
|
|
26
|
+
colors: {
|
|
27
|
+
skin: hex('#d9a07a'),
|
|
28
|
+
shade: hex('#a8644a'),
|
|
29
|
+
lips: hex('#b86b5c'),
|
|
30
|
+
sclera: hex('#ece6dc'),
|
|
31
|
+
iris: hex('#3d6e9e'),
|
|
32
|
+
pupil: hex('#141414'),
|
|
33
|
+
teeth: hex('#ece2c8'),
|
|
34
|
+
mouth: hex('#4a1a18'),
|
|
35
|
+
hair: hex('#5a3a22'),
|
|
36
|
+
cloth: hex('#2e4f8a'),
|
|
37
|
+
cape: hex('#7a2420'),
|
|
38
|
+
leather: hex('#6b4428'),
|
|
39
|
+
metal: hex('#c9a24a'),
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Massive underbitten jaw, tusks, heavy brow over small deep eyes, flat broad nose, thick neck. */
|
|
44
|
+
export const orc: HeadParams = {
|
|
45
|
+
height: 0.3,
|
|
46
|
+
cranium: { width: 0.7, depth: 0.74, height: 0.31 },
|
|
47
|
+
brow: { ridge: 0.075, overhang: 0.06, scowl: 0.04 },
|
|
48
|
+
cheeks: { size: 1.3, width: 1.15 },
|
|
49
|
+
jaw: { muzzle: 0.1, width: 0.4, chin: 1.3, underbite: 0.1, drop: 0.1 },
|
|
50
|
+
nose: { length: 0.15, width: 0.1, tip: 0.042, hook: -0.01, bridge: -0.05 },
|
|
51
|
+
ears: { shape: 'pointed', length: 0.38, width: 0.19, sweep: 30, lift: 10 },
|
|
52
|
+
eyes: { radius: 0.054, spacing: 0.155, line: 0.56, lid: 10, lowerLid: 22 },
|
|
53
|
+
mouth: { width: 0.17, lips: 0.034, grin: 0 },
|
|
54
|
+
tusks: { length: 0.22, radius: 0.055, spread: 0.15, flare: 0.35 },
|
|
55
|
+
neck: { radius: 0.38 },
|
|
56
|
+
bust: { shoulders: 1.0, style: 'pauldrons', mass: 1 },
|
|
57
|
+
hair: 'topknot',
|
|
58
|
+
beard: 'none',
|
|
59
|
+
warts: 6,
|
|
60
|
+
colors: {
|
|
61
|
+
skin: hex('#5c8034'),
|
|
62
|
+
shade: hex('#33501e'),
|
|
63
|
+
lips: hex('#4f6f30'),
|
|
64
|
+
sclera: hex('#8a5a1a'),
|
|
65
|
+
iris: hex('#ffb030'),
|
|
66
|
+
pupil: hex('#140a06'),
|
|
67
|
+
teeth: hex('#e2d6b0'),
|
|
68
|
+
mouth: hex('#2a0e0c'),
|
|
69
|
+
hair: hex('#231a14'),
|
|
70
|
+
cloth: hex('#6b2a20'),
|
|
71
|
+
leather: hex('#5a3a22'),
|
|
72
|
+
metal: hex('#55524d'),
|
|
73
|
+
fur: hex('#5e4a34'),
|
|
74
|
+
paint: hex('#8a1c12'),
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** Small and sly: huge leaf ears, long hooked nose, wide toothy grin, big bright eyes. */
|
|
79
|
+
export const goblin: HeadParams = {
|
|
80
|
+
height: 0.19,
|
|
81
|
+
cranium: { width: 0.66, depth: 0.84, height: 0.5 },
|
|
82
|
+
brow: { ridge: 0.032, overhang: 0.02, scowl: 0.03 },
|
|
83
|
+
cheeks: { size: 1.05, width: 1.05 },
|
|
84
|
+
jaw: { muzzle: 0.05, width: 0.19, chin: 0.72, underbite: 0, drop: 0 },
|
|
85
|
+
nose: { length: 0.2, width: 0.06, tip: 0.046, hook: 0.065, bridge: 0.13 },
|
|
86
|
+
ears: { shape: 'pointed', length: 0.66, width: 0.3, sweep: 24, lift: 6 },
|
|
87
|
+
eyes: { radius: 0.08, spacing: 0.155, line: 0.5, lid: 30, lowerLid: 20 },
|
|
88
|
+
mouth: { width: 0.21, lips: 0.014, grin: 0.8 },
|
|
89
|
+
tusks: null,
|
|
90
|
+
neck: { radius: 0.14 },
|
|
91
|
+
bust: { shoulders: 0.52, style: 'vest', mass: 0 },
|
|
92
|
+
hair: 'tufts',
|
|
93
|
+
beard: 'none',
|
|
94
|
+
warts: 4,
|
|
95
|
+
wrinkles: 3,
|
|
96
|
+
colors: {
|
|
97
|
+
skin: hex('#9cb84a'),
|
|
98
|
+
shade: hex('#6a8a2c'),
|
|
99
|
+
lips: hex('#7a8e36'),
|
|
100
|
+
sclera: hex('#f0e6a0'),
|
|
101
|
+
iris: hex('#e0a020'),
|
|
102
|
+
pupil: hex('#120c04'),
|
|
103
|
+
teeth: hex('#e8dcae'),
|
|
104
|
+
mouth: hex('#3a1010'),
|
|
105
|
+
hair: hex('#c8c0b0'),
|
|
106
|
+
cloth: hex('#b8a47c'),
|
|
107
|
+
cape: hex('#4e5e3a'),
|
|
108
|
+
leather: hex('#6b4428'),
|
|
109
|
+
metal: hex('#a88a4a'),
|
|
110
|
+
scarf: hex('#9a3a24'),
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** Broad and bearded: wide skull, big bulbous nose, braided beard, deep-set eyes. */
|
|
115
|
+
export const dwarf: HeadParams = {
|
|
116
|
+
...human,
|
|
117
|
+
height: 0.24,
|
|
118
|
+
cranium: { width: 0.74, depth: 0.86, height: 0.48 },
|
|
119
|
+
brow: { ridge: 0.05, overhang: 0.05, scowl: 0.02, hair: 0.024 },
|
|
120
|
+
cheeks: { size: 1.25, width: 1.1 },
|
|
121
|
+
jaw: { muzzle: 0.02, width: 0.27, chin: 1.2, underbite: 0, drop: 0.02 },
|
|
122
|
+
nose: { length: 0.2, width: 0.08, tip: 0.06, hook: 0.02, bridge: 0.02 },
|
|
123
|
+
eyes: { radius: 0.045, spacing: 0.14, line: 0.52, lid: 10, lowerLid: 24 },
|
|
124
|
+
neck: { radius: 0.34 },
|
|
125
|
+
bust: { shoulders: 1.1, style: 'tunic' },
|
|
126
|
+
hair: 'braids',
|
|
127
|
+
beard: 'braided',
|
|
128
|
+
colors: { ...human.colors, skin: hex('#d49470'), hair: hex('#8a3f1c'), cloth: hex('#4a5a2a') },
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/** Slender and ageless: long swept-back ears, high cheekbones, fine nose, almond eyes. */
|
|
132
|
+
export const elf: HeadParams = {
|
|
133
|
+
...human,
|
|
134
|
+
height: 0.235,
|
|
135
|
+
cranium: { width: 0.62, depth: 0.86, height: 0.52 },
|
|
136
|
+
brow: { ridge: 0.025, overhang: 0.02, scowl: -0.01, hair: 0.012 },
|
|
137
|
+
cheeks: { size: 1.1, width: 1.05 },
|
|
138
|
+
jaw: { muzzle: 0, width: 0.19, chin: 0.85, underbite: 0, drop: 0 },
|
|
139
|
+
nose: { length: 0.18, width: 0.045, tip: 0.028, hook: 0, bridge: 0.01 },
|
|
140
|
+
ears: { shape: 'pointed', length: 0.42, width: 0.16, sweep: 40, lift: 24 },
|
|
141
|
+
eyes: { radius: 0.05, spacing: 0.13, line: 0.5, lid: 14, lowerLid: 22 },
|
|
142
|
+
bust: { shoulders: 0.85, style: 'tunic' },
|
|
143
|
+
hair: 'swept',
|
|
144
|
+
beard: 'none',
|
|
145
|
+
colors: {
|
|
146
|
+
...human.colors,
|
|
147
|
+
skin: hex('#e8c0a0'),
|
|
148
|
+
hair: hex('#e0d090'),
|
|
149
|
+
iris: hex('#3a8a5a'),
|
|
150
|
+
cloth: hex('#2a6a4a'),
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const HEADS = { human, orc, goblin, dwarf, elf } as const;
|
|
155
|
+
export type HeadPreset = keyof typeof HEADS;
|