@developerz.ai/ts23d 0.1.0 → 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.
Files changed (162) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/CLAUDE.md +1 -1
  3. package/README.md +10 -5
  4. package/dist/{chunk-a8fxakcj.js → chunk-6b05mnmx.js} +2 -2
  5. package/dist/{chunk-kgvbpqqw.js → chunk-7rtfjh9f.js} +6 -5
  6. package/dist/chunk-7rtfjh9f.js.map +10 -0
  7. package/dist/{chunk-1ypfs7ga.js → chunk-8j4c28jt.js} +790 -276
  8. package/dist/chunk-8j4c28jt.js.map +46 -0
  9. package/dist/{chunk-wtqhq529.js → chunk-d1fv6hrm.js} +6 -6
  10. package/dist/{chunk-wtqhq529.js.map → chunk-d1fv6hrm.js.map} +1 -1
  11. package/dist/{chunk-gcyp9b4d.js → chunk-dbq4qf52.js} +362 -215
  12. package/dist/chunk-dbq4qf52.js.map +25 -0
  13. package/dist/{chunk-xnt2sdhf.js → chunk-drna2rny.js} +2 -2
  14. package/dist/{chunk-ravbb06q.js → chunk-dxvypr7y.js} +2 -2
  15. package/dist/{chunk-11v28977.js → chunk-f6yme003.js} +85 -71
  16. package/dist/{chunk-11v28977.js.map → chunk-f6yme003.js.map} +6 -6
  17. package/dist/{chunk-4ddzsqtc.js → chunk-hg7pf34j.js} +4 -3
  18. package/dist/{chunk-4ddzsqtc.js.map → chunk-hg7pf34j.js.map} +5 -5
  19. package/dist/{chunk-w1zyhj40.js → chunk-jq4e17tj.js} +2 -2
  20. package/dist/{chunk-kvcep7ft.js → chunk-y63c6zcv.js} +967 -218
  21. package/dist/chunk-y63c6zcv.js.map +53 -0
  22. package/dist/cli/args.d.ts +7 -1
  23. package/dist/cli/bin.js +66 -25
  24. package/dist/cli/bin.js.map +6 -5
  25. package/dist/cli/manifest.d.ts +5 -1
  26. package/dist/cli/texture-cache.d.ts +4 -0
  27. package/dist/core/definition.d.ts +7 -0
  28. package/dist/core/errors.d.ts +1 -1
  29. package/dist/core/index.js +1 -1
  30. package/dist/export/credits.d.ts +4 -0
  31. package/dist/export/glb-options.d.ts +48 -0
  32. package/dist/export/glb.d.ts +5 -35
  33. package/dist/export/hierarchy.d.ts +23 -0
  34. package/dist/export/index.d.ts +2 -1
  35. package/dist/export/index.js +21 -7
  36. package/dist/export/index.js.map +1 -1
  37. package/dist/export/lod-coverage.d.ts +12 -0
  38. package/dist/export/metadata.d.ts +2 -0
  39. package/dist/export/version.d.ts +2 -2
  40. package/dist/grammar/index.js +2 -2
  41. package/dist/index.js +63 -17
  42. package/dist/index.js.map +1 -1
  43. package/dist/kinds/head-armour.d.ts +3 -7
  44. package/dist/kinds/head-bust.d.ts +3 -2
  45. package/dist/kinds/head-face.d.ts +3 -1
  46. package/dist/kinds/head-garb.d.ts +4 -0
  47. package/dist/kinds/head-mesh.d.ts +4 -0
  48. package/dist/kinds/head-mouth.d.ts +4 -0
  49. package/dist/kinds/head-params.d.ts +9 -1
  50. package/dist/kinds/head-tone.d.ts +10 -0
  51. package/dist/kinds/head-torso.d.ts +35 -0
  52. package/dist/kinds/index.js +6 -6
  53. package/dist/material/cc0.d.ts +4 -0
  54. package/dist/material/define.d.ts +34 -0
  55. package/dist/material/generator.d.ts +1 -1
  56. package/dist/material/index.d.ts +8 -3
  57. package/dist/material/index.js +39 -7
  58. package/dist/material/index.js.map +1 -1
  59. package/dist/material/library.d.ts +7 -0
  60. package/dist/material/material-def.d.ts +30 -0
  61. package/dist/material/paint/canvas.d.ts +7 -0
  62. package/dist/material/paint/default-sheet.d.ts +3 -1
  63. package/dist/material/paint/fabric.d.ts +4 -0
  64. package/dist/material/paint/image-strip.d.ts +4 -0
  65. package/dist/material/paint/kit.d.ts +5 -0
  66. package/dist/material/paint/metal.d.ts +3 -0
  67. package/dist/material/paint/orm.d.ts +1 -1
  68. package/dist/material/paint/strip.d.ts +1 -1
  69. package/dist/material/paint/types.d.ts +32 -2
  70. package/dist/material/palette.d.ts +2 -0
  71. package/dist/material/slots.d.ts +5 -0
  72. package/dist/material/texture-source.d.ts +34 -0
  73. package/dist/material/trim-sheet.d.ts +20 -4
  74. package/dist/neural/index.js +2 -2
  75. package/dist/passes/index.js +2 -2
  76. package/dist/primitives/index.js +3 -3
  77. package/dist/qa/index.js +2 -2
  78. package/dist/render/beauty.d.ts +1 -1
  79. package/dist/render/index.js +3 -3
  80. package/dist/render/lighting.d.ts +7 -0
  81. package/dist/render/shade.d.ts +1 -1
  82. package/dist/testing/index.js +5 -5
  83. package/docs/README.md +4 -0
  84. package/docs/architecture/gltf-capabilities.md +38 -1
  85. package/docs/architecture/materials.md +151 -0
  86. package/docs/idea/10-sculpting.md +166 -0
  87. package/docs/idea/11-materials-authoring.md +132 -0
  88. package/docs/idea/12-blender-bridge.md +136 -0
  89. package/docs/idea/13-asset-pipeline-research.md +89 -0
  90. package/docs/plans/roadmap.md +12 -0
  91. package/package.json +2 -1
  92. package/recipes/boxcar/README.md +7 -0
  93. package/recipes/boxcar/boxcar.ts +118 -34
  94. package/recipes/goblin_head/README.md +27 -14
  95. package/recipes/goblin_head/goblin_head.ts +11 -9
  96. package/recipes/human_head/README.md +8 -4
  97. package/recipes/human_head/human_head.ts +4 -3
  98. package/recipes/material_swatches/material_swatches.ts +11 -5
  99. package/recipes/orc_head/README.md +32 -20
  100. package/recipes/orc_head/orc_head.ts +9 -7
  101. package/src/cli/args.ts +16 -3
  102. package/src/cli/manifest.ts +6 -0
  103. package/src/cli/run.ts +33 -9
  104. package/src/cli/texture-cache.ts +24 -0
  105. package/src/core/asset.ts +1 -0
  106. package/src/core/definition.ts +7 -0
  107. package/src/core/errors.ts +6 -1
  108. package/src/export/credits.ts +25 -0
  109. package/src/export/glb-options.ts +53 -0
  110. package/src/export/glb.ts +100 -113
  111. package/src/export/hierarchy.ts +122 -0
  112. package/src/export/index.ts +17 -1
  113. package/src/export/lod-coverage.ts +71 -0
  114. package/src/export/metadata.ts +3 -0
  115. package/src/export/version.ts +1 -1
  116. package/src/kinds/head-armour.ts +103 -73
  117. package/src/kinds/head-bust.ts +49 -86
  118. package/src/kinds/head-face.ts +94 -74
  119. package/src/kinds/head-garb.ts +181 -0
  120. package/src/kinds/head-hair.ts +74 -33
  121. package/src/kinds/head-mesh.ts +43 -5
  122. package/src/kinds/head-mouth.ts +111 -0
  123. package/src/kinds/head-params.ts +11 -2
  124. package/src/kinds/head-presets.ts +30 -27
  125. package/src/kinds/head-tone.ts +52 -0
  126. package/src/kinds/head-torso.ts +230 -0
  127. package/src/kinds/head.ts +8 -3
  128. package/src/material/cc0.ts +41 -0
  129. package/src/material/define.ts +130 -0
  130. package/src/material/generator.ts +15 -8
  131. package/src/material/index.ts +34 -3
  132. package/src/material/library.ts +196 -0
  133. package/src/material/material-def.ts +84 -0
  134. package/src/material/paint/canvas.ts +11 -1
  135. package/src/material/paint/compose.ts +1 -1
  136. package/src/material/paint/decode-png.ts +19 -13
  137. package/src/material/paint/default-sheet.ts +40 -28
  138. package/src/material/paint/fabric.ts +63 -0
  139. package/src/material/paint/image-strip.ts +88 -0
  140. package/src/material/paint/kit.ts +37 -0
  141. package/src/material/paint/masks.ts +3 -1
  142. package/src/material/paint/metal.ts +109 -0
  143. package/src/material/paint/orm.ts +14 -1
  144. package/src/material/paint/planks.ts +49 -4
  145. package/src/material/paint/strip.ts +35 -29
  146. package/src/material/paint/types.ts +45 -2
  147. package/src/material/paint/validate.ts +19 -3
  148. package/src/material/palette.ts +3 -0
  149. package/src/material/slots.ts +18 -0
  150. package/src/material/texture-source.ts +86 -0
  151. package/src/material/trim-sheet.ts +79 -13
  152. package/src/render/beauty.ts +2 -2
  153. package/src/render/lighting.ts +26 -0
  154. package/src/render/shade.ts +10 -5
  155. package/dist/chunk-1ypfs7ga.js.map +0 -42
  156. package/dist/chunk-gcyp9b4d.js.map +0 -22
  157. package/dist/chunk-kgvbpqqw.js.map +0 -10
  158. package/dist/chunk-kvcep7ft.js.map +0 -43
  159. /package/dist/{chunk-a8fxakcj.js.map → chunk-6b05mnmx.js.map} +0 -0
  160. /package/dist/{chunk-xnt2sdhf.js.map → chunk-drna2rny.js.map} +0 -0
  161. /package/dist/{chunk-ravbb06q.js.map → chunk-dxvypr7y.js.map} +0 -0
  162. /package/dist/{chunk-w1zyhj40.js.map → chunk-jq4e17tj.js.map} +0 -0
@@ -13,16 +13,16 @@ import {
13
13
  dot2,
14
14
  cross2,
15
15
  normalize2
16
- } from "./chunk-4ddzsqtc.js";
16
+ } from "./chunk-hg7pf34j.js";
17
17
  import {
18
18
  split2,
19
19
  parseWhyte2
20
- } from "./chunk-ravbb06q.js";
20
+ } from "./chunk-dxvypr7y.js";
21
21
  import {
22
- stripRect2,
22
+ hex2,
23
23
  DEFAULT_SHEET2,
24
- hex2
25
- } from "./chunk-kvcep7ft.js";
24
+ stripRect2
25
+ } from "./chunk-y63c6zcv.js";
26
26
  import {
27
27
  box2,
28
28
  lathe2,
@@ -38,10 +38,10 @@ import {
38
38
  arcLengths2,
39
39
  transportFrames2,
40
40
  sweep2
41
- } from "./chunk-wtqhq529.js";
41
+ } from "./chunk-d1fv6hrm.js";
42
42
  import {
43
43
  bakeAo2
44
- } from "./chunk-xnt2sdhf.js";
44
+ } from "./chunk-drna2rny.js";
45
45
 
46
46
  // src/kinds/faces.ts
47
47
  var EPS = 0.000000001;
@@ -531,17 +531,189 @@ function beardEdits(p, l) {
531
531
  return out;
532
532
  }
533
533
 
534
+ // src/kinds/head-torso.ts
535
+ var sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
536
+ var mid = (a, b) => [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2];
537
+ var dist = (a, b) => Math.hypot(...sub(a, b));
538
+ var PLINTH = 0.17;
539
+ function frameOf(p, l) {
540
+ const m = p.bust.mass ?? 0.5;
541
+ const b = l.base + 0.06 + 0.1 * m;
542
+ const nz = b + 0.6;
543
+ return {
544
+ cx: l.cranium.at[0] + p.jaw.muzzle * 0.3,
545
+ s: p.bust.shoulders,
546
+ b,
547
+ floor: l.base,
548
+ nz,
549
+ m,
550
+ n: p.neck.radius,
551
+ trapZ: nz + 0.12 + (l.hinge[1] - 0.2 - nz - 0.12) * m,
552
+ cutZ: l.base + PLINTH + 0.06 + 0.1 * m
553
+ };
554
+ }
555
+ function torsoForms(f, g = 0) {
556
+ const { cx, s, b, m, n } = f;
557
+ const grow = (r) => [r[0] + g, r[1] + g, r[2] + g];
558
+ const top = [cx - 0.12, n * 0.5, f.trapZ];
559
+ const acromion = [cx - 0.05, s * 0.78, b + 0.62 + 0.05 * m];
560
+ return [
561
+ {
562
+ shape: "ellipsoid",
563
+ at: [cx - 0.05, 0, b + 0.36],
564
+ size: grow([0.25 + 0.07 * m, s * 0.66, 0.38])
565
+ },
566
+ {
567
+ shape: "ellipsoid",
568
+ at: [cx + 0.1 + 0.05 * m, s * 0.27, b + 0.46 + 0.02 * m],
569
+ size: grow([0.09 + 0.06 * m, s * 0.27, 0.11 + 0.03 * m]),
570
+ rotate: [-12, 0, 16],
571
+ blend: 0.05
572
+ },
573
+ {
574
+ shape: "ellipsoid",
575
+ at: [cx - 0.03, s * 0.8, b + 0.5 + 0.03 * m],
576
+ size: grow([0.13 + 0.06 * m, 0.11 + 0.05 * m, 0.16 + 0.05 * m]),
577
+ rotate: [-18, 0, 0],
578
+ blend: 0.08
579
+ },
580
+ {
581
+ shape: "ellipsoid",
582
+ at: mid(top, acromion),
583
+ size: [dist(top, acromion) / 2 + 0.08 + g, 0.11 + 0.12 * m + g, 0.05 + 0.1 * m + g],
584
+ rotate: alongX(sub(acromion, top)),
585
+ blend: 0.08 + 0.08 * m
586
+ },
587
+ {
588
+ shape: "capsule",
589
+ at: [cx - 0.04, s * 0.8, b + 0.55],
590
+ to: [cx - 0.02, s * 0.9, b + 0.05],
591
+ size: 0.12 + 0.05 * m + g,
592
+ blend: 0.06
593
+ },
594
+ {
595
+ shape: "capsule",
596
+ at: [cx + 0.2 + 0.04 * m, 0.045, f.nz - 0.03],
597
+ to: [cx + 0, s * 0.68, b + 0.6 + 0.04 * m],
598
+ size: 0.02 + 0.006 * (1 - m) + g,
599
+ blend: 0.035
600
+ },
601
+ {
602
+ shape: "ellipsoid",
603
+ at: [cx - 0.1, s * 0.55, b + 0.3],
604
+ size: grow([0.17 + 0.05 * m, 0.12 + 0.1 * m, 0.2]),
605
+ blend: 0.08
606
+ },
607
+ {
608
+ shape: "ellipsoid",
609
+ at: [cx - 0.18, 0, f.nz + 0.04 + 0.08 * m],
610
+ size: grow([0.12 + 0.06 * m, s * 0.32, 0.16 + 0.12 * m]),
611
+ blend: 0.1
612
+ }
613
+ ];
614
+ }
615
+ function cutField(f) {
616
+ const rz = 0.64;
617
+ const ry = f.s * 1.08;
618
+ const cz = f.cutZ + rz;
619
+ const e = sd2.ellipsoid([f.cx, 0, cz], [1.6, ry, rz]);
620
+ const box = sd2.box([f.cx, 0, cz + 1], [3.2, ry * 2, 2]);
621
+ return (q) => Math.min(e(q), box(q));
622
+ }
623
+ function bustCut(f, g = 0) {
624
+ const rz = 0.64;
625
+ const ry = f.s * 1.08 + g;
626
+ const cz = f.cutZ + rz - g;
627
+ return {
628
+ group: [
629
+ { shape: "ellipsoid", at: [f.cx, 0, cz], size: [1.6, ry, rz] },
630
+ { shape: "roundBox", at: [f.cx, 0, cz + 1], size: [3.2, ry * 2, 2] }
631
+ ],
632
+ op: "intersect",
633
+ blend: 0.012
634
+ };
635
+ }
636
+ function bodyEdits(p, f, color) {
637
+ const { cx, b, m, nz } = f;
638
+ const shade = p.colors.shade;
639
+ return [
640
+ {
641
+ group: [
642
+ ...torsoForms(f),
643
+ {
644
+ shape: "sphere",
645
+ at: [cx + 0.24 + 0.04 * m, 0, nz - 0.04],
646
+ size: 0.035,
647
+ op: "subtract",
648
+ blend: 0.04,
649
+ color: shade
650
+ },
651
+ {
652
+ shape: "capsule",
653
+ at: [cx + 0.26 + 0.06 * m, 0, b + 0.5],
654
+ to: [cx + 0.26 + 0.05 * m, 0, b + 0.22],
655
+ size: 0.01 + 0.006 * m,
656
+ op: "subtract",
657
+ blend: 0.04,
658
+ color: shade
659
+ },
660
+ bustCut(f)
661
+ ],
662
+ color
663
+ }
664
+ ];
665
+ }
666
+ var cylinder = (at, r, z0, z1) => ({
667
+ group: [
668
+ { shape: "capsule", at: [at[0], at[1], z0 - r], to: [at[0], at[1], z1 + r], size: r },
669
+ {
670
+ shape: "roundBox",
671
+ at: [at[0], at[1], (z0 + z1) / 2],
672
+ size: [4, 4, z1 - z0],
673
+ op: "intersect"
674
+ }
675
+ ]
676
+ });
677
+ function plinthEdits(p, f, minT = 0) {
678
+ const c = p.colors.plinth ?? [0.16, 0.15, 0.15];
679
+ const light = [c[0] * 1.35, c[1] * 1.35, c[2] * 1.35];
680
+ const x = f.cx - 0.04;
681
+ const R = 0.3 + 0.08 * f.s;
682
+ if (minT > 0.07)
683
+ return [
684
+ { ...cylinder([x, 0, 0], R * 1.05, f.floor - 0.1, f.cutZ + 0.02), color: c, mirror: false }
685
+ ];
686
+ const disc = f.floor + 0.07;
687
+ return [
688
+ { ...cylinder([x, 0, 0], R, f.floor - 0.1, disc), color: c, mirror: false },
689
+ {
690
+ shape: "torus",
691
+ at: [x, 0, disc],
692
+ size: [R - 0.03, 0.025],
693
+ color: light,
694
+ blend: 0.01,
695
+ mirror: false
696
+ },
697
+ {
698
+ ...cylinder([x, 0, 0], R * 0.62, disc, f.cutZ + 0.02),
699
+ color: c,
700
+ blend: 0.02,
701
+ mirror: false
702
+ }
703
+ ];
704
+ }
705
+
534
706
  // src/kinds/head-armour.ts
535
707
  var RAD2 = Math.PI / 180;
536
708
  var shade = (c, k) => [c[0] * k, c[1] * k, c[2] * k];
537
- var above = (p, deg, op = "intersect") => {
709
+ var above = (p, deg) => {
538
710
  const n = [0, -Math.sin(deg * RAD2), Math.cos(deg * RAD2)];
539
711
  return {
540
712
  shape: "roundBox",
541
713
  at: [p[0] + n[0], p[1] + n[1], p[2] + n[2]],
542
714
  size: [4, 4, 2],
543
715
  rotate: [deg, 0, 0],
544
- op
716
+ op: "intersect"
545
717
  };
546
718
  };
547
719
  var slab = (p, deg, t) => ({
@@ -551,73 +723,97 @@ var slab = (p, deg, t) => ({
551
723
  rotate: [deg, 0, 0],
552
724
  op: "intersect"
553
725
  });
554
- var chestOf = (cx, s, b) => ({
555
- at: [cx - 0.04, 0, b + 0.2],
556
- r: [0.36, s * 0.75, 0.42]
726
+ var ell = (at, r, g, roll) => ({
727
+ shape: "ellipsoid",
728
+ at,
729
+ size: [r[0] + g, r[1] + g, r[2] + g],
730
+ rotate: [roll, 0, 0]
557
731
  });
558
- function armour(p, cx, s, b, nz) {
732
+ function plate(at, r, roll, cut, tilt, c, t) {
733
+ return [
734
+ {
735
+ group: [
736
+ ell(at, r, 0, roll),
737
+ ...t < Math.min(...r) * 0.6 ? [{ ...ell(at, r, -t, roll), op: "subtract" }] : [],
738
+ above(cut, tilt)
739
+ ],
740
+ color: c,
741
+ blend: 0.006
742
+ },
743
+ {
744
+ group: [
745
+ ell(at, r, 0.022, roll),
746
+ ...t < Math.min(...r) * 0.6 ? [{ ...ell(at, r, -t * 0.8, roll), op: "subtract" }] : [],
747
+ slab(cut, tilt, 0.05)
748
+ ],
749
+ color: shade(c, 1.3),
750
+ blend: 0.006
751
+ }
752
+ ];
753
+ }
754
+ function armour(p, f, cell = 0) {
755
+ const t = Math.max(0.085, cell * 2.5);
559
756
  const c = p.colors;
757
+ const { cx, s, b, nz } = f;
560
758
  const metal = c.metal;
561
759
  const out = [];
562
- for (let k = 0;k < 3; k++) {
563
- const at = [cx - 0.03, s * (0.78 + k * 0.1), b + 0.53 - k * 0.12];
564
- const r = [0.31 - k * 0.02, 0.27 - k * 0.03, 0.19 - k * 0.02];
565
- const tilt = -(28 + k * 14);
566
- const cut = [at[0], at[1], at[2] - r[2] * 0.35];
567
- out.push({
568
- group: [{ shape: "ellipsoid", at, size: r }, above(cut, tilt)],
569
- color: shade(metal, 1 - k * 0.12),
570
- blend: 0.008
571
- }, {
572
- group: [
573
- { shape: "ellipsoid", at, size: [r[0] + 0.018, r[1] + 0.018, r[2] + 0.018] },
574
- slab(cut, tilt, 0.035)
575
- ],
576
- color: shade(metal, 0.55),
577
- blend: 0.004
578
- });
760
+ const cap = [cx - 0.04, s * 0.8, b + 0.66];
761
+ for (let k = 1;k >= 0; k--) {
762
+ const at = [cap[0], cap[1] + k * 0.07 * s, cap[2] - k * 0.17];
763
+ const r = k === 0 ? [0.27, 0.29, 0.25] : [0.23, 0.25, 0.19];
764
+ const tilt = -(38 + k * 14);
765
+ const cut = [at[0], at[1], at[2] - r[2] * 0.45];
766
+ out.push(...plate(at, r, -25 - k * 10, cut, tilt, shade(metal, 1 - k * 0.15), t));
579
767
  for (let j = 0;j < 4; j++) {
580
- const t = (j - 1.5) * 0.11;
768
+ const x = at[0] + (j - 1.5) * 0.12;
581
769
  out.push({
582
770
  shape: "sphere",
583
- at: [at[0] + t, at[1] + Math.sin(-tilt * RAD2) * r[1] * 0.55, cut[2] + 0.03],
584
- size: 0.016,
585
- color: shade(metal, 1.4),
771
+ at: [x, at[1] + r[1] * 0.62, cut[2] + 0.02 - r[1] * 0.62 * Math.tan(-tilt * RAD2) * 0.4],
772
+ size: 0.017,
773
+ color: shade(metal, 1.35),
586
774
  blend: 0.004
587
775
  });
588
776
  }
589
777
  }
778
+ const crest = [cap[0], cap[1] - 0.03, cap[2] + 0.25];
779
+ out.push({
780
+ shape: "ellipsoid",
781
+ at: crest,
782
+ size: [0.25, 0.028, 0.07],
783
+ rotate: [-22, 0, 0],
784
+ color: shade(metal, 0.75),
785
+ blend: 0.02
786
+ });
590
787
  for (let k = 0;k < 3; k++)
591
788
  out.push({
592
789
  shape: "cone",
593
- at: [cx - 0.17 + k * 0.14, s * 0.84, b + 0.68 - Math.abs(k - 1) * 0.03],
594
- size: [0.065, 0.006, 0.26 - Math.abs(k - 1) * 0.08],
595
- rotate: alongZ(unit([(k - 1) * 0.35, 0.45, 1])),
790
+ at: [crest[0] - 0.14 + k * 0.14, crest[1] + 0.01, crest[2] + 0.02 - Math.abs(k - 1) * 0.03],
791
+ size: [0.075, 0.012, 0.36 - Math.abs(k - 1) * 0.12],
792
+ rotate: alongZ(unit([(k - 1) * 0.3, 0.55, 1])),
596
793
  color: c.teeth,
597
- blend: 0.01
794
+ blend: 0.015
598
795
  });
599
- const chest = chestOf(cx, s, b);
600
- const grow = (g) => [chest.r[0] + g, chest.r[1] + g, chest.r[2] + g];
601
- const band = [cx, 0, b + 0.28];
796
+ const band = [cx, 0, b + 0.36];
602
797
  out.push({
603
- group: [{ shape: "ellipsoid", at: chest.at, size: grow(0.03) }, slab(band, 52, 0.17)],
798
+ group: [{ group: torsoForms(f, 0.03) }, slab(band, 50, 0.15), bustCut(f)],
604
799
  color: c.leather,
605
800
  blend: 0.006,
606
801
  mirror: false
607
802
  }, {
608
803
  group: [
609
- { shape: "ellipsoid", at: chest.at, size: grow(0.055) },
804
+ { group: torsoForms(f, 0.055) },
805
+ bustCut(f),
610
806
  {
611
807
  shape: "roundBox",
612
- at: [cx + 0.3, 0.02, b + 0.3],
613
- size: [0.3, 0.15, 0.15],
614
- rotate: [52, 0, 0],
808
+ at: [cx + 0.3, 0.02, b + 0.38],
809
+ size: [0.3, 0.14, 0.14],
810
+ rotate: [50, 0, 0],
615
811
  op: "intersect"
616
812
  },
617
813
  {
618
814
  shape: "ellipsoid",
619
- at: [cx + 0.42, 0.02, b + 0.3],
620
- size: [0.1, 0.045, 0.045],
815
+ at: [cx + 0.42, 0.02, b + 0.38],
816
+ size: [0.1, 0.04, 0.04],
621
817
  op: "subtract"
622
818
  }
623
819
  ],
@@ -626,60 +822,193 @@ function armour(p, cx, s, b, nz) {
626
822
  mirror: false
627
823
  });
628
824
  const fur = c.fur ?? c.leather;
629
- const R = p.neck.radius + 0.1;
630
- out.push({
631
- shape: "torus",
632
- at: [cx - 0.08, 0, nz + 0.02],
633
- size: [R, 0.07],
634
- color: shade(fur, 0.75),
635
- blend: 0.04
636
- });
637
- for (let row = 0;row < 2; row++)
638
- for (let i = 0;i < 10; i++) {
639
- const a = (-85 + i * 20 + row * 10) * RAD2;
640
- const rr = R + row * 0.05;
825
+ const R = f.n + 0.08;
826
+ for (let row = 0;row < 3; row++)
827
+ for (let i = 0;i < 8; i++) {
828
+ const a = (-80 + i * 22 + row * 11) * RAD2;
829
+ const rr = R + row * 0.07;
641
830
  const root = [
642
- cx - 0.08 + Math.cos(a) * rr,
643
- Math.abs(Math.sin(a)) * rr + 0.015,
644
- nz + 0.07 - row * 0.06
831
+ cx - 0.1 - Math.abs(Math.cos(a)) * rr * 0.9,
832
+ Math.abs(Math.sin(a)) * rr + 0.02,
833
+ nz + 0.16 - row * 0.05
645
834
  ];
646
- const dir = unit([Math.cos(a), Math.abs(Math.sin(a)), -0.5 - (i + row) % 2 * 0.35]);
835
+ const dir = unit([-0.7, Math.abs(Math.sin(a)) * 0.8, -0.55 - (i + row) % 2 * 0.3]);
647
836
  out.push({
648
837
  shape: "cone",
649
838
  at: root,
650
- size: [0.07 - row * 0.01, 0.012, 0.13 + (i + row) % 3 * 0.03],
839
+ size: [0.075 - row * 0.008, 0.012, 0.16 + (i + row) % 3 * 0.04],
651
840
  rotate: alongZ(dir),
652
- color: (i + row) % 2 ? fur : shade(fur, 0.7),
653
- blend: 0.03
841
+ color: (i + row) % 2 ? fur : shade(fur, 0.72),
842
+ blend: 0.035
654
843
  });
655
844
  }
656
845
  return out;
657
846
  }
658
847
 
659
- // src/kinds/head-bust.ts
660
- function torso(p, cx, b) {
848
+ // src/kinds/head-garb.ts
849
+ var shade2 = (c, k) => [c[0] * k, c[1] * k, c[2] * k];
850
+ function vest(p, f) {
661
851
  const c = p.colors;
662
- return [
852
+ const { cx, s, b, nz, n } = f;
853
+ const scarf = c.scarf ?? c.cloth;
854
+ const hood = c.cape ?? shade2(c.cloth, 0.8);
855
+ const out = [
663
856
  {
664
- shape: "ellipsoid",
665
- at: [cx + 0.17, 0.16, b + 0.32],
666
- size: [0.16, 0.19, 0.12],
667
- rotate: [0, 0, 12],
668
- color: c.skin,
669
- blend: 0.07
857
+ group: [
858
+ { group: torsoForms(f, 0.032) },
859
+ {
860
+ shape: "ellipsoid",
861
+ at: [cx + 0.32, 0, nz + 0.01],
862
+ size: [0.2, 0.07, 0.11],
863
+ op: "subtract",
864
+ blend: 0.01
865
+ },
866
+ bustCut(f)
867
+ ],
868
+ color: c.cloth,
869
+ blend: 0.006
670
870
  },
671
871
  {
672
- shape: "capsule",
673
- at: [cx + 0.33, 0, b + 0.42],
674
- to: [cx + 0.34, 0, b + 0.1],
675
- size: 0.012,
676
- op: "subtract",
872
+ group: [
873
+ { group: torsoForms(f, 0.065) },
874
+ {
875
+ shape: "roundBox",
876
+ at: [cx + 0.4, 0, b + 0.3],
877
+ size: [0.5, 0.3, 1],
878
+ rotate: [0, -8, 0],
879
+ round: 0.08,
880
+ op: "subtract",
881
+ blend: 0.02
882
+ },
883
+ {
884
+ shape: "sphere",
885
+ at: [cx - 0.03, s * 0.98, b + 0.42],
886
+ size: 0.17,
887
+ op: "subtract",
888
+ blend: 0.02
889
+ },
890
+ bustCut(f)
891
+ ],
892
+ color: c.leather,
893
+ blend: 0.006
894
+ },
895
+ {
896
+ group: [
897
+ { group: torsoForms(f, 0.088) },
898
+ {
899
+ shape: "roundBox",
900
+ at: [cx, 0, b + 0.38],
901
+ size: [4, 4, 0.08],
902
+ rotate: [-48, 0, 0],
903
+ op: "intersect"
904
+ },
905
+ bustCut(f)
906
+ ],
907
+ color: shade2(c.leather, 0.65),
908
+ blend: 0.004,
909
+ mirror: false
910
+ },
911
+ {
912
+ group: [
913
+ { group: torsoForms(f, 0.105) },
914
+ {
915
+ shape: "roundBox",
916
+ at: [cx + 0.3, -0.06, b + 0.32],
917
+ size: [0.3, 0.1, 0.1],
918
+ rotate: [-48, 0, 0],
919
+ op: "intersect"
920
+ },
921
+ {
922
+ shape: "roundBox",
923
+ at: [cx + 0.4, -0.06, b + 0.32],
924
+ size: [0.16, 0.05, 0.05],
925
+ rotate: [-48, 0, 0],
926
+ op: "subtract"
927
+ }
928
+ ],
929
+ color: c.metal,
930
+ blend: 0.003,
931
+ mirror: false
932
+ },
933
+ {
934
+ group: [
935
+ {
936
+ shape: "torus",
937
+ at: [cx - 0.1, 0, nz + 0.03],
938
+ size: [n + 0.12, 0.06],
939
+ rotate: [0, -28, 0]
940
+ },
941
+ {
942
+ shape: "roundBox",
943
+ at: [cx - 1.04, 0, nz],
944
+ size: [2, 4, 4],
945
+ op: "intersect",
946
+ blend: 0.04
947
+ }
948
+ ],
949
+ color: hood,
950
+ blend: 0.03
951
+ },
952
+ {
953
+ group: [
954
+ { shape: "ellipsoid", at: [cx - 0.33, 0, nz - 0.04], size: [0.13, s * 0.5, 0.25] },
955
+ {
956
+ shape: "cone",
957
+ at: [cx - 0.36, 0, nz - 0.12],
958
+ size: [0.1, 0.015, 0.34],
959
+ rotate: alongZ(unit([-0.35, 0, -1])),
960
+ blend: 0.05
961
+ },
962
+ {
963
+ shape: "ellipsoid",
964
+ at: [cx - 0.3, 0, nz + 0.12],
965
+ size: [0.08, s * 0.36, 0.1],
966
+ op: "subtract",
967
+ blend: 0.02,
968
+ color: shade2(hood, 0.35)
969
+ }
970
+ ],
971
+ color: hood,
677
972
  blend: 0.04,
678
- color: c.shade,
679
973
  mirror: false
974
+ },
975
+ {
976
+ shape: "torus",
977
+ at: [cx - 0.04, 0, nz + 0.17],
978
+ size: [n + 0.05, 0.065],
979
+ rotate: [0, -12, 0],
980
+ color: scarf,
981
+ blend: 0.02
680
982
  }
681
983
  ];
984
+ const knot = [cx + n * 0.8, n + 0.04, nz + 0.13];
985
+ out.push({
986
+ shape: "ellipsoid",
987
+ at: knot,
988
+ size: [0.05, 0.06, 0.05],
989
+ color: shade2(scarf, 0.85),
990
+ blend: 0.02,
991
+ mirror: false
992
+ });
993
+ for (const [dy, len, tw] of [
994
+ [0.3, 0.2, 0.045],
995
+ [0.6, 0.16, 0.04]
996
+ ]) {
997
+ const dir = unit([0.3, dy, -1]);
998
+ out.push({
999
+ shape: "cone",
1000
+ at: [knot[0] + 0.01, knot[1] + dy * 0.04, knot[2] - 0.02],
1001
+ size: [tw, tw * 0.7, len],
1002
+ rotate: alongZ(dir),
1003
+ color: dy > 0.5 ? scarf : shade2(scarf, 0.8),
1004
+ blend: 0.015,
1005
+ mirror: false
1006
+ });
1007
+ }
1008
+ return out;
682
1009
  }
1010
+
1011
+ // src/kinds/head-bust.ts
683
1012
  function hood(p, cx, nz) {
684
1013
  const c = p.colors;
685
1014
  return [
@@ -725,14 +1054,17 @@ function hood(p, cx, nz) {
725
1054
  }
726
1055
  ];
727
1056
  }
728
- function tunic(p, cx, s, b, nz) {
1057
+ function tunic(p, f) {
729
1058
  const c = p.colors;
730
- const chest = chestOf(cx, s, b);
731
- const shell = (g) => ({
732
- shape: "ellipsoid",
733
- at: chest.at,
734
- size: [chest.r[0] + g, chest.r[1] + g, chest.r[2] + g]
735
- });
1059
+ const { cx, s, b, nz } = f;
1060
+ const shell = (g) => ({ group: torsoForms(f, g) });
1061
+ const torso = sculptField2(torsoForms(f), { color: c.cloth, mirror: "y" }).field;
1062
+ const front = (z) => {
1063
+ let x = cx + 0.7;
1064
+ for (let i = 0;i < 300 && torso([x, 0, z]) > 0; i++)
1065
+ x -= 0.004;
1066
+ return x;
1067
+ };
736
1068
  const n = p.neck.radius;
737
1069
  const axis = (z) => [cx - 0.08, 0, z];
738
1070
  const mantle = (g) => ({
@@ -760,7 +1092,8 @@ function tunic(p, cx, s, b, nz) {
760
1092
  group: [
761
1093
  mantle(0),
762
1094
  { shape: "roundBox", at: [0, 0, b + 0.3 + 1], size: [4, 4, 2], op: "intersect" },
763
- opening
1095
+ opening,
1096
+ bustCut(f)
764
1097
  ],
765
1098
  color: c.cape ?? c.leather,
766
1099
  blend: 0.01
@@ -796,66 +1129,41 @@ function tunic(p, cx, s, b, nz) {
796
1129
  blend: 0.004,
797
1130
  mirror: false
798
1131
  },
799
- ...[0.42, 0.3, 0.18].map((z) => ({
800
- group: [
801
- shell(0.03),
802
- { shape: "sphere", at: [cx + 0.3, 0, b + z], size: 0.03, op: "intersect" }
803
- ],
1132
+ ...[0.5, 0.38, 0.26].map((z) => ({
1133
+ shape: "sphere",
1134
+ at: [front(b + z) + 0.014, 0, b + z],
1135
+ size: 0.028,
804
1136
  color: c.metal,
805
- blend: 0.003,
1137
+ blend: 0.004,
806
1138
  mirror: false
807
1139
  }))
808
1140
  ];
809
1141
  }
810
- function bustEdits(p, l) {
1142
+ function bustEdits(p, l, cell = 0) {
811
1143
  const c = p.colors;
812
- const cx = l.cranium.at[0] + p.jaw.muzzle * 0.3;
813
- const s = p.bust.shoulders;
814
- const b = l.base;
815
- const nz = b + 0.6;
816
- const body = p.bust.style === "pauldrons" ? c.skin : c.cloth;
1144
+ const f = frameOf(p, l);
1145
+ const style = p.bust.style;
1146
+ const bare = style === "pauldrons" || style === "vest";
817
1147
  const e = [
818
- { shape: "ellipsoid", at: chestOf(cx, s, b).at, size: chestOf(cx, s, b).r, color: body },
819
- {
820
- shape: "capsule",
821
- at: [cx - 0.16, 0.1, nz + 0.16],
822
- to: [cx - 0.1, s * 0.78, b + 0.44],
823
- size: 0.15,
824
- color: body,
825
- blend: 0.15
826
- },
827
- { shape: "sphere", at: [cx - 0.04, s * 0.84, b + 0.3], size: 0.2, color: body, blend: 0.1 },
828
- {
829
- shape: "capsule",
830
- at: [cx - 0.2, 0.08, nz + 0.3],
831
- to: [cx - 0.1, s * 0.62, b + 0.5],
832
- size: 0.1 + p.neck.radius * 0.2,
833
- color: body,
834
- blend: 0.12
835
- },
1148
+ ...bodyEdits(p, f, bare ? c.skin : c.cloth),
836
1149
  {
837
- shape: "capsule",
838
- at: [cx - 0.08, 0, nz - 0.05],
839
- to: [cx - 0.1, 0, nz + 0.12],
840
- size: p.neck.radius * 1.05,
1150
+ shape: "ellipsoid",
1151
+ at: [f.cx - 0.09, 0, f.nz + 0.06],
1152
+ size: [f.n * 1.02, f.n * 1.02, 0.12],
841
1153
  color: c.skin,
842
- blend: 0.03
1154
+ blend: 0.04
843
1155
  }
844
1156
  ];
845
- e.push({
846
- shape: "roundBox",
847
- at: [0, s + 1.02, b + 0.5],
848
- size: [4, 2, 4],
849
- op: "subtract",
850
- color: body
851
- });
852
- if (p.bust.style === "pauldrons")
853
- e.push(...torso(p, cx, b), ...armour(p, cx, s, b, nz));
854
- else if (p.bust.style === "hood")
855
- e.push(...hood(p, cx, nz));
1157
+ if (style === "pauldrons")
1158
+ e.push(...armour(p, f, cell));
1159
+ else if (style === "hood")
1160
+ e.push(...hood(p, f.cx, f.nz));
1161
+ else if (style === "vest")
1162
+ e.push(...vest(p, f));
856
1163
  else
857
- e.push(...tunic(p, cx, s, b, nz));
858
- e.push({ shape: "roundBox", at: [0, 0, b - 1], size: [4, 4, 2], op: "subtract" });
1164
+ e.push(...tunic(p, f));
1165
+ e.push(...plinthEdits(p, f, cell * 1.5));
1166
+ e.push({ shape: "roundBox", at: [0, 0, f.floor - 1], size: [4, 4, 2], op: "subtract" });
859
1167
  return e;
860
1168
  }
861
1169
 
@@ -990,6 +1298,100 @@ function wartEdits(p, l, rng) {
990
1298
  });
991
1299
  }
992
1300
 
1301
+ // src/kinds/head-mouth.ts
1302
+ function mouthEdits(p, l, teeth = true) {
1303
+ const c = p.colors;
1304
+ const lipT = p.mouth.lips;
1305
+ const mw = p.mouth.width;
1306
+ const g = p.mouth.grin;
1307
+ const out = [
1308
+ {
1309
+ shape: "ellipsoid",
1310
+ at: [l.mouthX - 0.01, 0, l.mouthZ + lipT * 0.7],
1311
+ size: [0.032, mw, lipT],
1312
+ color: c.lips,
1313
+ blend: 0.03
1314
+ },
1315
+ {
1316
+ shape: "ellipsoid",
1317
+ at: [l.mouthX - 0.015 + p.jaw.underbite * 0.7, 0, l.mouthZ - lipT * 0.8],
1318
+ size: [0.034 + p.jaw.underbite * 0.3, mw * 0.85, lipT * (1.1 + p.jaw.underbite * 4)],
1319
+ color: c.lips,
1320
+ blend: 0.03
1321
+ }
1322
+ ];
1323
+ if (g < 0.4 || !teeth) {
1324
+ out.push({
1325
+ shape: "capsule",
1326
+ at: [l.mouthX + 0.05, 0, l.mouthZ],
1327
+ to: [l.mouthX - 0.03 - 0.02 * g, mw * 0.98, l.mouthZ + 0.012 + g * 0.05 - p.brow.scowl * 0.4],
1328
+ size: 0.006 + g * 0.022,
1329
+ op: "subtract",
1330
+ blend: 0.012,
1331
+ color: c.mouth
1332
+ });
1333
+ return out;
1334
+ }
1335
+ const h = 0.035 + g * 0.05;
1336
+ const z0 = l.mouthZ + h * 0.45;
1337
+ const back = mw * 0.55;
1338
+ out.push({
1339
+ group: [
1340
+ { shape: "ellipsoid", at: [l.mouthX - back, 0, z0], size: [back + 0.06, mw, h] },
1341
+ {
1342
+ shape: "ellipsoid",
1343
+ at: [l.mouthX - back + 0.02, 0, z0 + h * 0.7],
1344
+ size: [back + 0.12, mw * 1.1, h * 0.9],
1345
+ op: "subtract"
1346
+ }
1347
+ ],
1348
+ op: "subtract",
1349
+ blend: 0.012,
1350
+ color: c.mouth
1351
+ });
1352
+ if (teeth)
1353
+ out.push({
1354
+ shape: "ellipsoid",
1355
+ at: [l.mouthX - back * 0.9, 0, l.mouthZ - h * 0.4],
1356
+ size: [back * 0.9, mw * 0.62, h * 0.45],
1357
+ color: [0.42, 0.12, 0.12],
1358
+ blend: 0.01
1359
+ });
1360
+ out.push({
1361
+ shape: "ellipsoid",
1362
+ at: [l.mouthX - 0.06, mw * 0.95, z0 + h * 0.6],
1363
+ size: [0.05, 0.045, 0.04 + g * 0.01],
1364
+ rotate: [-30, 0, 0],
1365
+ blend: 0.035
1366
+ });
1367
+ const n = teeth ? 3 : 0;
1368
+ for (let i = 0;i < n; i++) {
1369
+ const a = (0.25 + i) * 0.36;
1370
+ const y = Math.sin(a) * mw * 0.82;
1371
+ const x = l.mouthX - 0.03 - (1 - Math.cos(a)) * back * 1.3;
1372
+ const lo = z0 - h * Math.sqrt(Math.max(0, 1 - (y / mw) ** 2));
1373
+ const hi = z0 + h * 0.7 - h * 0.9 * Math.sqrt(Math.max(0, 1 - (y / (mw * 1.1)) ** 2));
1374
+ const len = 0.03 + g * 0.008 - i * 0.004;
1375
+ const r = 0.014 + g * 0.004 - i * 0.0015;
1376
+ out.push({
1377
+ shape: "cone",
1378
+ at: [x, y, hi + 0.008],
1379
+ size: [r, 0.003, len],
1380
+ rotate: alongZ([0.15, 0, -1]),
1381
+ color: c.teeth,
1382
+ blend: 0.004
1383
+ }, {
1384
+ shape: "cone",
1385
+ at: [x - 0.006, y * 0.97, lo - 0.008],
1386
+ size: [r * 0.85, 0.003, len * 0.6],
1387
+ rotate: alongZ([0.1, 0, 1]),
1388
+ color: c.teeth,
1389
+ blend: 0.004
1390
+ });
1391
+ }
1392
+ return out;
1393
+ }
1394
+
993
1395
  // src/kinds/head-face.ts
994
1396
  function primaryEdits(l) {
995
1397
  const [cx, , cz] = l.cranium.at;
@@ -1004,26 +1406,36 @@ function primaryEdits(l) {
1004
1406
  }
1005
1407
  ];
1006
1408
  }
1007
- function skullEdits(p, l) {
1409
+ function skullEdits(p, l, cell = 0) {
1410
+ const fine = cell < 0.03;
1411
+ const tipR = Math.max(p.nose.tip, cell * 0.6);
1008
1412
  const c = p.colors;
1009
1413
  const cx = l.cranium.at[0];
1010
1414
  const [ex, ey, ez] = l.eye;
1011
1415
  const r = l.eyeR;
1012
- const lipT = p.mouth.lips;
1013
1416
  const mw = p.mouth.width;
1014
1417
  const [ax, ay, az] = l.angle;
1015
1418
  const [hx, hz] = l.hinge;
1016
1419
  const chin = l.chin;
1017
1420
  const tipX = l.faceX + 0.06 + p.nose.length * 0.25 + p.nose.bridge + p.jaw.muzzle * 0.5;
1018
1421
  const tipZ = l.noseZ + 0.035 - p.nose.hook;
1422
+ const bIn = [ex + r * 0.75, 0.03, l.browZ - p.brow.scowl];
1423
+ const bOut = [ex + r * 0.05, ey + r * 1.3, l.browZ + p.brow.scowl * 0.5];
1019
1424
  const e = [
1020
1425
  ...primaryEdits(l),
1021
1426
  {
1022
1427
  shape: "ellipsoid",
1023
1428
  at: [ex - 0.03, (ey + 0.06) * p.cheeks.width, ez - r - 0.065 * p.cheeks.size],
1024
- size: [0.09 * p.cheeks.size, 0.08 * p.cheeks.size, 0.065 * p.cheeks.size],
1429
+ size: [0.075 * p.cheeks.size, 0.07 * p.cheeks.size, 0.055 * p.cheeks.size],
1025
1430
  rotate: [0, 0, 25],
1026
- blend: 0.09
1431
+ blend: 0.08
1432
+ },
1433
+ {
1434
+ shape: "capsule",
1435
+ at: [ex - r * 0.2, (ey + 0.05) * p.cheeks.width, ez - r - 0.03],
1436
+ to: [cx + 0.06, l.cranium.r[1] * 0.92, ez - 0.07],
1437
+ size: 0.028 * p.cheeks.size,
1438
+ blend: 0.05
1027
1439
  },
1028
1440
  {
1029
1441
  shape: "ellipsoid",
@@ -1054,9 +1466,10 @@ function skullEdits(p, l) {
1054
1466
  },
1055
1467
  { shape: "sphere", at: [ax + 0.02, ay * 0.95, az], size: 0.065, blend: 0.07 },
1056
1468
  {
1057
- shape: "ellipsoid",
1469
+ shape: "roundBox",
1058
1470
  at: chin,
1059
- size: [0.065 * p.jaw.chin, 0.09 * p.jaw.chin, 0.07 * p.jaw.chin],
1471
+ size: [0.13 * p.jaw.chin, 0.18 * p.jaw.chin, 0.14 * p.jaw.chin],
1472
+ round: 0.06 * p.jaw.chin,
1060
1473
  blend: 0.06
1061
1474
  },
1062
1475
  {
@@ -1068,10 +1481,27 @@ function skullEdits(p, l) {
1068
1481
  },
1069
1482
  {
1070
1483
  shape: "capsule",
1071
- at: [ex + r * 0.75, 0.025, l.browZ - p.brow.scowl],
1072
- to: [ex + r * 0.1, ey + r * 1.25, l.browZ + p.brow.scowl * 0.5],
1073
- size: p.brow.ridge,
1074
- blend: 0.06
1484
+ at: [cx - 0.03, l.cranium.r[1] * 0.68, ez - 0.28],
1485
+ to: [cx + p.neck.radius * 0.6, 0.05, l.base + 0.74],
1486
+ size: p.neck.radius * 0.26,
1487
+ blend: 0.05
1488
+ },
1489
+ {
1490
+ shape: "ellipsoid",
1491
+ at: [(bIn[0] + bOut[0]) / 2, (bIn[1] + bOut[1]) / 2, (bIn[2] + bOut[2]) / 2],
1492
+ size: [
1493
+ Math.hypot(bOut[0] - bIn[0], bOut[1] - bIn[1], bOut[2] - bIn[2]) / 2 + p.brow.ridge * 0.6,
1494
+ p.brow.ridge * 1.25,
1495
+ p.brow.ridge * 0.85
1496
+ ],
1497
+ rotate: alongX([bOut[0] - bIn[0], bOut[1] - bIn[1], bOut[2] - bIn[2]]),
1498
+ blend: 0.05
1499
+ },
1500
+ {
1501
+ shape: "sphere",
1502
+ at: [bIn[0] - 0.01, 0, bIn[2] - p.brow.ridge * 0.15],
1503
+ size: p.brow.ridge * 1.05,
1504
+ blend: 0.05
1075
1505
  },
1076
1506
  { shape: "sphere", at: l.eye, size: r * 1.12, op: "subtract", blend: r * 0.5, color: c.shade }
1077
1507
  ];
@@ -1094,56 +1524,50 @@ function skullEdits(p, l) {
1094
1524
  };
1095
1525
  };
1096
1526
  e.push(lid(p.eyes.lid, true), lid(p.eyes.lowerLid, false));
1527
+ const long = p.nose.bridge > 0.06 ? 0.65 : 1;
1528
+ const wing = Math.min(1.4, Math.max(0.55, p.nose.width / 0.065)) * long;
1529
+ const rootR = Math.max(0.02 + p.nose.width * 0.6, cell * 0.65);
1530
+ const wingX = Math.min(tipX - p.nose.tip * 1.2, l.faceX + 0.1 + p.jaw.muzzle * 0.5);
1097
1531
  const noseRoot = [ex + r * 0.4, 0, ez - r * 0.4];
1098
1532
  const noseDir = [tipX - noseRoot[0], 0, tipZ + 0.01 - noseRoot[2]];
1099
1533
  const midNose = [
1100
1534
  (ex + r * 0.6 + tipX) / 2,
1101
1535
  0,
1102
- (ez - 0.02 + tipZ) / 2 + p.nose.hook * 0.6 + 0.01
1536
+ (ez - 0.02 + tipZ) / 2 + p.nose.hook * 0.3 + 0.01
1103
1537
  ];
1104
1538
  e.push({
1105
1539
  shape: "cone",
1106
1540
  at: noseRoot,
1107
- size: [0.02 + p.nose.width * 0.6, p.nose.tip * 0.8, Math.hypot(...noseDir)],
1541
+ size: [rootR, tipR * 0.85, Math.hypot(...noseDir)],
1108
1542
  rotate: alongZ(noseDir),
1109
- blend: 0.04
1110
- }, { shape: "sphere", at: midNose, size: 0.02 + p.nose.width * 0.2, blend: 0.04 }, { shape: "sphere", at: [tipX, 0, tipZ], size: p.nose.tip, blend: 0.03 }, {
1543
+ blend: 0.045
1544
+ }, {
1545
+ shape: "sphere",
1546
+ at: midNose,
1547
+ size: (rootR + tipR) / 2 * 0.85 + Math.max(0, p.nose.hook) * 0.12,
1548
+ blend: 0.05
1549
+ }, { shape: "sphere", at: [tipX, 0, tipZ], size: tipR, blend: 0.035 }, ...p.nose.hook > 0.04 ? [
1550
+ {
1551
+ shape: "cone",
1552
+ at: [tipX, 0, tipZ],
1553
+ size: [tipR * 0.9, 0.01, p.nose.tip + p.nose.hook * 1.2],
1554
+ rotate: alongZ([0.6, 0, -1]),
1555
+ blend: 0.03
1556
+ }
1557
+ ] : [], {
1111
1558
  shape: "ellipsoid",
1112
- at: [tipX - p.nose.tip * 1.2, p.nose.width, l.noseZ + 0.02],
1113
- size: [0.045, 0.035, 0.032],
1559
+ at: [wingX, p.nose.width, l.noseZ + 0.02],
1560
+ size: [0.045 * wing, 0.035 * wing, 0.032 * wing],
1114
1561
  blend: 0.025
1115
1562
  }, {
1116
1563
  shape: "ellipsoid",
1117
- at: [tipX - p.nose.tip * 0.6, p.nose.width * 0.55, l.noseZ + 0.005],
1118
- size: [0.022, 0.014, 0.012],
1564
+ at: [Math.min(tipX - p.nose.tip * 0.6, wingX + 0.035), p.nose.width * 0.55, l.noseZ + 0.005],
1565
+ size: [0.022 * wing, 0.014 * wing, 0.012 * wing],
1119
1566
  op: "subtract",
1120
1567
  blend: 0.008,
1121
1568
  color: c.mouth
1122
1569
  });
1123
- const g = p.mouth.grin;
1124
- e.push({
1125
- shape: "ellipsoid",
1126
- at: [l.mouthX - 0.01, 0, l.mouthZ + lipT * 0.7],
1127
- size: [0.032, mw, lipT],
1128
- color: c.lips,
1129
- blend: 0.03
1130
- }, {
1131
- shape: "ellipsoid",
1132
- at: [l.mouthX - 0.015 + p.jaw.underbite * 0.7, 0, l.mouthZ - lipT * 0.8],
1133
- size: [0.034, mw * 0.85, lipT * 1.1],
1134
- color: c.lips,
1135
- blend: 0.03
1136
- }, {
1137
- shape: "capsule",
1138
- at: [l.mouthX + 0.05, 0, l.mouthZ],
1139
- to: [l.mouthX - 0.03 - 0.02 * g, mw * 0.98, l.mouthZ + 0.012 + g * 0.05],
1140
- size: 0.006 + g * 0.022,
1141
- op: "subtract",
1142
- blend: 0.012,
1143
- color: c.mouth
1144
- });
1145
- if (g > 0.15)
1146
- e.push(...teethRows(p, l));
1570
+ e.push(...mouthEdits(p, l, fine));
1147
1571
  const fold = unit([-0.02, mw - p.nose.width, l.mouthZ - l.noseZ]);
1148
1572
  e.push({
1149
1573
  shape: "ellipsoid",
@@ -1154,31 +1578,22 @@ function skullEdits(p, l) {
1154
1578
  blend: 0.03,
1155
1579
  color: c.shade
1156
1580
  });
1157
- return e;
1158
- }
1159
- function teethRows(p, l) {
1160
- const out = [];
1161
- const mw = p.mouth.width * 0.8;
1162
- for (let i = 0;i < 4; i++) {
1163
- const a = (i + 0.5) / 4 * 0.9;
1164
- const y = Math.sin(a) * mw;
1165
- const x = l.mouthX - 0.025 - (1 - Math.cos(a)) * mw * 0.9;
1166
- const z = l.mouthZ + 0.004 + p.mouth.grin * 0.03 * (i / 4);
1167
- for (const [dz, h] of [
1168
- [0.016, 0.02],
1169
- [-0.016, 0.018]
1170
- ])
1171
- out.push({
1172
- shape: "roundBox",
1173
- at: [x, y, z + dz * (1 + p.mouth.grin)],
1174
- size: [0.02, 0.028, h * (1 + p.mouth.grin * 0.6)],
1175
- round: 0.006,
1176
- rotate: [0, 0, a * 180 / Math.PI],
1177
- color: p.colors.teeth,
1178
- blend: 0.003
1179
- });
1581
+ const [, , cz] = l.cranium.at;
1582
+ const [crx, , crz] = l.cranium.r;
1583
+ for (let i = 0;i < (p.wrinkles ?? 0); i++) {
1584
+ const z = l.browZ + 0.07 + i * 0.05;
1585
+ const x = cx + crx * Math.sqrt(Math.max(0, 1 - ((z - cz) / crz) ** 2)) + 0.005;
1586
+ e.push({
1587
+ shape: "ellipsoid",
1588
+ at: [x, 0.06, z - 0.008],
1589
+ size: [0.05, 0.08 - i * 0.012, 0.005],
1590
+ rotate: [-14, -12, 0],
1591
+ op: "paint",
1592
+ blend: 0.01,
1593
+ color: c.shade
1594
+ });
1180
1595
  }
1181
- return out;
1596
+ return e;
1182
1597
  }
1183
1598
 
1184
1599
  // src/kinds/head-hair.ts
@@ -1189,31 +1604,40 @@ function hairEdits(p, l) {
1189
1604
  const [rx, ry, rz] = l.cranium.r;
1190
1605
  const top = l.crown;
1191
1606
  switch (p.hair) {
1192
- case "topknot":
1607
+ case "topknot": {
1608
+ const root = [cx - 0.14, 0, top - 0.03];
1609
+ const rise = [cx - 0.2, 0, top + 0.13];
1610
+ const fall = [cx - 0.38, 0, top + 0.02];
1611
+ const tip = [cx - rx - 0.08, 0, cz - 0.12];
1612
+ const seg = (a, b, r0, r1) => ({
1613
+ shape: "cone",
1614
+ at: a,
1615
+ size: [r0, r1, Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2])],
1616
+ rotate: alongZ([b[0] - a[0], b[1] - a[1], b[2] - a[2]]),
1617
+ blend: 0.03
1618
+ });
1619
+ const ring = (at, deg) => ({
1620
+ shape: "torus",
1621
+ at,
1622
+ size: [0.07, 0.018],
1623
+ rotate: [0, deg, 0],
1624
+ color: p.colors.metal,
1625
+ blend: 0.004
1626
+ });
1193
1627
  return [
1194
- { shape: "ellipsoid", at: [cx - 0.12, 0, top - 0.02], size: [0.11, 0.08, 0.07], color: h },
1195
1628
  {
1196
- shape: "sphere",
1197
- at: [cx - 0.16, 0, top + 0.07],
1198
- size: 0.075,
1199
- color: h,
1200
- blend: 0.03
1201
- },
1202
- {
1203
- shape: "capsule",
1204
- at: [cx - 0.2, 0, top + 0.08],
1205
- to: [cx - 0.42, 0, top - 0.25],
1206
- size: 0.04,
1629
+ group: [
1630
+ seg(root, rise, 0.085, 0.075),
1631
+ seg(rise, fall, 0.08, 0.07),
1632
+ seg(fall, tip, 0.07, 0.015)
1633
+ ],
1207
1634
  color: h,
1208
- blend: 0.04
1635
+ blend: 0.02
1209
1636
  },
1210
- {
1211
- shape: "torus",
1212
- at: [cx - 0.17, 0, top + 0.02],
1213
- size: [0.06, 0.018],
1214
- color: p.colors.metal
1215
- }
1637
+ ring([root[0] - 0.02, 0, root[2] + 0.06], -15),
1638
+ ring([root[0] - 0.04, 0, root[2] + 0.11], -25)
1216
1639
  ];
1640
+ }
1217
1641
  case "mohawk":
1218
1642
  return Array.from({ length: 6 }, (_, i) => {
1219
1643
  const a = (-20 + i * 26) * RAD4;
@@ -1282,20 +1706,48 @@ function hairEdits(p, l) {
1282
1706
  lock(120, 30, 30, 0.05, 0)
1283
1707
  ];
1284
1708
  }
1285
- case "tufts":
1286
- return Array.from({ length: 5 }, (_, i) => {
1287
- const a = (i / 4 - 0.5) * 1.6;
1288
- const base = [cx - 0.1 + Math.cos(a) * 0.05, Math.sin(a) * 0.12, top - 0.04];
1289
- return {
1290
- shape: "cone",
1291
- at: base,
1292
- size: [0.025, 0.004, 0.12 + i % 2 * 0.05],
1293
- rotate: alongZ([-0.6, Math.sin(a) * 0.8, 1]),
1709
+ case "tufts": {
1710
+ const out = [];
1711
+ const spots = [
1712
+ [10, 78, 0.26],
1713
+ [-25, 70, 0.22],
1714
+ [40, 62, 0.22],
1715
+ [150, 55, 0.2],
1716
+ [115, 30, 0.2],
1717
+ [100, 16, 0.18],
1718
+ [-120, 40, 0.18]
1719
+ ];
1720
+ for (const [az, el, len] of spots) {
1721
+ const a = az * RAD4;
1722
+ const e = el * RAD4;
1723
+ const n = [Math.cos(e) * Math.cos(a), Math.cos(e) * Math.sin(a), Math.sin(e)];
1724
+ const root = [cx - 0.04 + rx * 0.97 * n[0], ry * 0.97 * n[1], cz + rz * 0.97 * n[2]];
1725
+ const d1 = [n[0] - 0.6, n[1] * 1.3, n[2] * 0.6 + 0.25];
1726
+ const l1 = Math.hypot(...d1);
1727
+ const mid = [
1728
+ root[0] + d1[0] / l1 * len * 0.55,
1729
+ root[1] + d1[1] / l1 * len * 0.55,
1730
+ root[2] + d1[2] / l1 * len * 0.55
1731
+ ];
1732
+ const d2 = [d1[0] - 0.5, d1[1] * 1.6, -0.9];
1733
+ out.push({
1734
+ group: [
1735
+ { shape: "cone", at: root, size: [0.026, 0.014, len * 0.58], rotate: alongZ(d1) },
1736
+ {
1737
+ shape: "cone",
1738
+ at: mid,
1739
+ size: [0.015, 0.004, len * 0.6],
1740
+ rotate: alongZ(d2),
1741
+ blend: 0.008
1742
+ }
1743
+ ],
1294
1744
  color: h,
1295
- blend: 0.01,
1296
- mirror: false
1297
- };
1298
- });
1745
+ blend: 0.012,
1746
+ mirror: az !== 10 && az !== -25 && az !== -120
1747
+ });
1748
+ }
1749
+ return out;
1750
+ }
1299
1751
  case "braids":
1300
1752
  return [
1301
1753
  { ...capOf(l, h) },
@@ -1461,10 +1913,46 @@ function creaseNormals(m, minDot = 0.2) {
1461
1913
  return { ...m, normals };
1462
1914
  }
1463
1915
 
1916
+ // src/kinds/head-tone.ts
1917
+ function toned(field, colorAt, o) {
1918
+ const k = o.strength ?? 1;
1919
+ const d = o.reach;
1920
+ const e = d * 0.25;
1921
+ return (q) => {
1922
+ const c = colorAt(q);
1923
+ const [x, y, z] = q;
1924
+ const gx = field([x + e, y, z]) - field([x - e, y, z]);
1925
+ const gy = field([x, y + e, z]) - field([x, y - e, z]);
1926
+ const gz = field([x, y, z + e]) - field([x, y, z - e]);
1927
+ const gl = Math.hypot(gx, gy, gz) || 1;
1928
+ const n = [gx / gl, gy / gl, gz / gl];
1929
+ let occ = 0;
1930
+ for (let i = 1;i <= 3; i++) {
1931
+ const t = d * i / 3;
1932
+ occ += Math.max(0, t - field([x + n[0] * t, y + n[1] * t, z + n[2] * t])) / t / i;
1933
+ }
1934
+ const cav = Math.min(1, occ * 2.5) * k;
1935
+ const up = n[2] * k;
1936
+ const lift = up > 0 ? 1 + up * 0.22 : 1 + up * 0.3;
1937
+ const warm = up > 0 ? [1 + up * 0.12, 1 + up * 0.05, 1 - up * 0.15] : [1 + up * 0.1, 1, 1 - up * 0.12];
1938
+ const dark = 1 - cav * 0.55;
1939
+ const lum = (c[0] + c[1] + c[2]) / 3;
1940
+ const sat = 1 + cav * 0.35;
1941
+ return [
1942
+ clamp((lum + (c[0] - lum) * sat) * dark * lift * warm[0]),
1943
+ clamp((lum + (c[1] - lum) * sat) * dark * lift * warm[1]),
1944
+ clamp((lum + (c[2] - lum) * sat) * dark * lift * warm[2])
1945
+ ];
1946
+ };
1947
+ }
1948
+ var clamp = (v) => Math.min(Math.max(v, 0), 1);
1949
+
1464
1950
  // src/kinds/head-mesh.ts
1951
+ var REACH = 0.05;
1952
+ var PLINTH2 = [0.16, 0.15, 0.15];
1465
1953
  function jawRegion(p, l) {
1466
1954
  const chin = l.chin;
1467
- const reach = Math.max(p.mouth.width * 1.25, (p.tusks?.spread ?? 0) + p.mouth.width * 0.5);
1955
+ const reach = Math.max(p.mouth.width * 1.08, (p.tusks?.spread ?? 0) + p.mouth.width * 0.5);
1468
1956
  const centre = [l.mouthX - 0.15, 0, l.mouthZ];
1469
1957
  const radii = [Math.max(0.27, l.mouthX - chin[0] + 0.2), reach, l.mouthZ - chin[2] + 0.1];
1470
1958
  const e = sd2.ellipsoid(centre, radii);
@@ -1496,7 +1984,7 @@ function headAndJaw(p, l, set, res) {
1496
1984
  const region = jawRegion(p, l);
1497
1985
  const cut = region.field;
1498
1986
  const lap = res * 0.75;
1499
- const proud = res * 0.15;
1987
+ const proud = res * 0.03;
1500
1988
  const ears = set.ears.length > 0 ? [{ group: set.ears, blend: 0.02 }] : [];
1501
1989
  const tusks = set.tusks.length > 0 ? [{ group: set.tusks, blend: 0.01 }] : [];
1502
1990
  const h = sculptField2([...set.skull, ...ears, { ...regionGroup(l, region, 0), op: "subtract" }], opts);
@@ -1504,6 +1992,9 @@ function headAndJaw(p, l, set, res) {
1504
1992
  const jField = (q) => jRaw.field(q) - proud;
1505
1993
  const mouth = p.colors.mouth;
1506
1994
  const skull = sculptField2(set.skull, opts).field;
1995
+ const extra = (e) => e.length > 0 ? ((f) => (q) => Math.min(skull(q), f(q)))(sculptField2(e, opts).field) : skull;
1996
+ const headTone = extra(set.ears);
1997
+ const jawTone = extra(set.tusks);
1507
1998
  const deep = -Math.max(res * 4, 0.03);
1508
1999
  const paint = (colorAt) => (q) => Math.abs(cut(q)) < res * 1.5 && skull(q) < deep ? mouth : colorAt(q);
1509
2000
  const [ec, er] = [region.centre, region.radii];
@@ -1516,14 +2007,14 @@ function headAndJaw(p, l, set, res) {
1516
2007
  field: h.field,
1517
2008
  bounds: h.bounds,
1518
2009
  resolution: res,
1519
- color: paint(h.colorAt),
2010
+ color: toned(headTone, paint(h.colorAt), { reach: REACH }),
1520
2011
  symmetry: "y"
1521
2012
  });
1522
2013
  const jaw = sdf2({
1523
2014
  field: jField,
1524
2015
  bounds: unionBounds(jb, tb),
1525
2016
  resolution: res,
1526
- color: paint(jRaw.colorAt),
2017
+ color: toned(jawTone, paint(jRaw.colorAt), { reach: REACH }),
1527
2018
  symmetry: "y"
1528
2019
  });
1529
2020
  if (res > 0.03)
@@ -1534,13 +2025,29 @@ function headAndJaw(p, l, set, res) {
1534
2025
  jaw: creaseNormals(sharpenRim(jaw, jawSkull, (q) => cut(q) - lap, jField, res))
1535
2026
  };
1536
2027
  }
2028
+ function onCut(cut, q, n, res) {
2029
+ if (Math.abs(cut(q)) > res * 0.8)
2030
+ return false;
2031
+ const e = res * 0.5;
2032
+ const g = [
2033
+ cut([q[0] + e, q[1], q[2]]) - cut([q[0] - e, q[1], q[2]]),
2034
+ cut([q[0], q[1] + e, q[2]]) - cut([q[0], q[1] - e, q[2]]),
2035
+ cut([q[0], q[1], q[2] + e]) - cut([q[0], q[1], q[2] - e])
2036
+ ];
2037
+ const l = Math.hypot(g[0], g[1], g[2]) || 1;
2038
+ return (g[0] * n[0] + g[1] * n[1] + g[2] * n[2]) / l > 0.85;
2039
+ }
1537
2040
  function bustMesh(p, set, res) {
1538
2041
  const s = sculptField2(set.bust, { color: p.colors.skin, mirror: "y" });
2042
+ const tone = toned(s.field, s.colorAt, { reach: REACH * 1.4 });
2043
+ const cut = set.bustCut;
2044
+ const base = p.colors.plinth ?? PLINTH2;
2045
+ const face = [base[0] * 1.3, base[1] * 1.3, base[2] * 1.3];
1539
2046
  return sdf2({
1540
2047
  field: s.field,
1541
2048
  bounds: s.bounds,
1542
2049
  resolution: res,
1543
- color: (q) => s.colorAt(q),
2050
+ color: cut ? (q, n) => onCut(cut, q, n, res) ? face : tone(q) : tone,
1544
2051
  symmetry: "y"
1545
2052
  });
1546
2053
  }
@@ -1594,7 +2101,9 @@ var ZERO_OK = new Set([
1594
2101
  "jaw.drop",
1595
2102
  "jaw.muzzle",
1596
2103
  "brow.overhang",
1597
- "brow.hair"
2104
+ "brow.hair",
2105
+ "bust.mass",
2106
+ "wrinkles"
1598
2107
  ]);
1599
2108
  function checkHead2(p) {
1600
2109
  if (!(p.height >= 0.05 && p.height <= 3))
@@ -1658,33 +2167,33 @@ var human = {
1658
2167
  };
1659
2168
  var orc = {
1660
2169
  height: 0.3,
1661
- cranium: { width: 0.7, depth: 0.8, height: 0.34 },
1662
- brow: { ridge: 0.065, overhang: 0.075, scowl: 0.035 },
1663
- cheeks: { size: 1.35, width: 1.15 },
1664
- jaw: { muzzle: 0.1, width: 0.37, chin: 1.6, underbite: 0.07, drop: 0.1 },
1665
- nose: { length: 0.15, width: 0.09, tip: 0.04, hook: -0.01, bridge: -0.045 },
1666
- ears: { shape: "pointed", length: 0.3, width: 0.17, sweep: 25, lift: 8 },
1667
- eyes: { radius: 0.05, spacing: 0.155, line: 0.56, lid: 16, lowerLid: 26 },
1668
- mouth: { width: 0.17, lips: 0.03, grin: 0 },
1669
- tusks: { length: 0.27, radius: 0.05, spread: 0.14, flare: 0.35 },
1670
- neck: { radius: 0.32 },
1671
- bust: { shoulders: 1.05, style: "pauldrons" },
2170
+ cranium: { width: 0.7, depth: 0.74, height: 0.31 },
2171
+ brow: { ridge: 0.075, overhang: 0.06, scowl: 0.04 },
2172
+ cheeks: { size: 1.3, width: 1.15 },
2173
+ jaw: { muzzle: 0.1, width: 0.4, chin: 1.3, underbite: 0.1, drop: 0.1 },
2174
+ nose: { length: 0.15, width: 0.1, tip: 0.042, hook: -0.01, bridge: -0.05 },
2175
+ ears: { shape: "pointed", length: 0.38, width: 0.19, sweep: 30, lift: 10 },
2176
+ eyes: { radius: 0.054, spacing: 0.155, line: 0.56, lid: 10, lowerLid: 22 },
2177
+ mouth: { width: 0.17, lips: 0.034, grin: 0 },
2178
+ tusks: { length: 0.22, radius: 0.055, spread: 0.15, flare: 0.35 },
2179
+ neck: { radius: 0.38 },
2180
+ bust: { shoulders: 1, style: "pauldrons", mass: 1 },
1672
2181
  hair: "topknot",
1673
2182
  beard: "none",
1674
2183
  warts: 6,
1675
2184
  colors: {
1676
- skin: hex2("#5f8b3a"),
1677
- shade: hex2("#3c5c24"),
2185
+ skin: hex2("#5c8034"),
2186
+ shade: hex2("#33501e"),
1678
2187
  lips: hex2("#4f6f30"),
1679
2188
  sclera: hex2("#8a5a1a"),
1680
2189
  iris: hex2("#ffb030"),
1681
2190
  pupil: hex2("#140a06"),
1682
2191
  teeth: hex2("#e2d6b0"),
1683
2192
  mouth: hex2("#2a0e0c"),
1684
- hair: hex2("#1c1714"),
2193
+ hair: hex2("#231a14"),
1685
2194
  cloth: hex2("#6b2a20"),
1686
2195
  leather: hex2("#5a3a22"),
1687
- metal: hex2("#6e6a62"),
2196
+ metal: hex2("#55524d"),
1688
2197
  fur: hex2("#5e4a34"),
1689
2198
  paint: hex2("#8a1c12")
1690
2199
  }
@@ -1692,19 +2201,20 @@ var orc = {
1692
2201
  var goblin = {
1693
2202
  height: 0.19,
1694
2203
  cranium: { width: 0.66, depth: 0.84, height: 0.5 },
1695
- brow: { ridge: 0.03, overhang: 0.015, scowl: 0.025 },
1696
- cheeks: { size: 1.15, width: 1.1 },
1697
- jaw: { muzzle: 0.06, width: 0.2, chin: 0.85, underbite: 0, drop: 0 },
1698
- nose: { length: 0.2, width: 0.07, tip: 0.05, hook: 0.07, bridge: 0.12 },
1699
- ears: { shape: "pointed", length: 0.62, width: 0.3, sweep: 22, lift: 18 },
1700
- eyes: { radius: 0.072, spacing: 0.15, line: 0.5, lid: 16, lowerLid: 26 },
1701
- mouth: { width: 0.2, lips: 0.016, grin: 0.75 },
2204
+ brow: { ridge: 0.032, overhang: 0.02, scowl: 0.03 },
2205
+ cheeks: { size: 1.05, width: 1.05 },
2206
+ jaw: { muzzle: 0.05, width: 0.19, chin: 0.72, underbite: 0, drop: 0 },
2207
+ nose: { length: 0.2, width: 0.06, tip: 0.046, hook: 0.065, bridge: 0.13 },
2208
+ ears: { shape: "pointed", length: 0.66, width: 0.3, sweep: 24, lift: 6 },
2209
+ eyes: { radius: 0.08, spacing: 0.155, line: 0.5, lid: 30, lowerLid: 20 },
2210
+ mouth: { width: 0.21, lips: 0.014, grin: 0.8 },
1702
2211
  tusks: null,
1703
- neck: { radius: 0.2 },
1704
- bust: { shoulders: 0.75, style: "hood" },
2212
+ neck: { radius: 0.14 },
2213
+ bust: { shoulders: 0.52, style: "vest", mass: 0 },
1705
2214
  hair: "tufts",
1706
2215
  beard: "none",
1707
2216
  warts: 4,
2217
+ wrinkles: 3,
1708
2218
  colors: {
1709
2219
  skin: hex2("#9cb84a"),
1710
2220
  shade: hex2("#6a8a2c"),
@@ -1714,10 +2224,12 @@ var goblin = {
1714
2224
  pupil: hex2("#120c04"),
1715
2225
  teeth: hex2("#e8dcae"),
1716
2226
  mouth: hex2("#3a1010"),
1717
- hair: hex2("#3a3024"),
1718
- cloth: hex2("#6a4a7a"),
1719
- leather: hex2("#5a3a22"),
1720
- metal: hex2("#a88a4a")
2227
+ hair: hex2("#c8c0b0"),
2228
+ cloth: hex2("#b8a47c"),
2229
+ cape: hex2("#4e5e3a"),
2230
+ leather: hex2("#6b4428"),
2231
+ metal: hex2("#a88a4a"),
2232
+ scarf: hex2("#9a3a24")
1721
2233
  }
1722
2234
  };
1723
2235
  var dwarf = {
@@ -1783,6 +2295,7 @@ function seatEyes(p, l) {
1783
2295
  const r = l.eyeR;
1784
2296
  return { ...l, eye: [x - r * 0.7 - p.brow.overhang * 0.3, ey, ez] };
1785
2297
  }
2298
+ var BUST_CELL = 1.6;
1786
2299
  var HEAD_RES2 = [0.02, 0.045, 0.14];
1787
2300
  function headEdits2(p, rng, lod = 0) {
1788
2301
  const l = seatEyes(p, headLayout(p));
@@ -1792,7 +2305,7 @@ function headEdits2(p, rng, lod = 0) {
1792
2305
  return {
1793
2306
  layout: l,
1794
2307
  skull: [
1795
- ...skullEdits(p, l),
2308
+ ...skullEdits(p, l, res),
1796
2309
  ...hair,
1797
2310
  ...browEdits(p, l),
1798
2311
  ...beardEdits(p, l),
@@ -1801,7 +2314,8 @@ function headEdits2(p, rng, lod = 0) {
1801
2314
  ],
1802
2315
  ears: earEdits(p, l, res * 0.7),
1803
2316
  tusks: tuskEdits(p, l, res * 0.45),
1804
- bust: bustEdits(p, l)
2317
+ bust: bustEdits(p, l, res * BUST_CELL),
2318
+ bustCut: cutField(frameOf(p, l))
1805
2319
  };
1806
2320
  }
1807
2321
  function head2(ctx, o = {}) {
@@ -1811,7 +2325,7 @@ function head2(ctx, o = {}) {
1811
2325
  const H = p.height;
1812
2326
  const res = HEAD_RES2[ctx.lod];
1813
2327
  const { head: hm, jaw: jm } = headAndJaw(p, l, set, res);
1814
- const bm = bustMesh(p, set, res * 1.6);
2328
+ const bm = bustMesh(p, set, res * BUST_CELL);
1815
2329
  const eye = eyeMesh(p, l.eyeR, l.eyeR / ctx.pick([7, 3.5, 2.2]));
1816
2330
  const [ox, oy, oz] = o.at ?? [0, 0, 0];
1817
2331
  const toWorld = (m, at = [0, 0, 0]) => place2(m, { scale: H, at: [ox + at[0] * H, oy + at[1] * H, oz + (at[2] - l.base) * H] });
@@ -3567,5 +4081,5 @@ function propeller2(ctx, o) {
3567
4081
  }
3568
4082
  export { mass2, opening2, facade2, setbacks2, checkHead2, HEADS2, headParams2, HEAD_RES2, headEdits2, head2, hullLines2, hullMesh2, hull2, creature2, roofHeightAt2, ridgeHeight2, roofMesh2, roof2, roofInfo2, oak2, pine2, palm2, birch2, willow2, SPECIES2, treeParams2, tree2, treeReal2, trees2, axle2, whyteAxles2, trackRun2, propeller2 };
3569
4083
 
3570
- //# debugId=5C8F10A02B6E9AB264756E2164756E21
3571
- //# sourceMappingURL=chunk-1ypfs7ga.js.map
4084
+ //# debugId=AF041752F55E532164756E2164756E21
4085
+ //# sourceMappingURL=chunk-8j4c28jt.js.map