@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.
- package/CHANGELOG.md +11 -0
- package/CLAUDE.md +1 -1
- package/README.md +10 -5
- package/dist/{chunk-a8fxakcj.js → chunk-6b05mnmx.js} +2 -2
- package/dist/{chunk-kgvbpqqw.js → chunk-7rtfjh9f.js} +6 -5
- package/dist/chunk-7rtfjh9f.js.map +10 -0
- package/dist/{chunk-1ypfs7ga.js → chunk-8j4c28jt.js} +790 -276
- package/dist/chunk-8j4c28jt.js.map +46 -0
- package/dist/{chunk-wtqhq529.js → chunk-d1fv6hrm.js} +6 -6
- package/dist/{chunk-wtqhq529.js.map → chunk-d1fv6hrm.js.map} +1 -1
- package/dist/{chunk-gcyp9b4d.js → chunk-dbq4qf52.js} +362 -215
- package/dist/chunk-dbq4qf52.js.map +25 -0
- package/dist/{chunk-xnt2sdhf.js → chunk-drna2rny.js} +2 -2
- package/dist/{chunk-ravbb06q.js → chunk-dxvypr7y.js} +2 -2
- package/dist/{chunk-11v28977.js → chunk-f6yme003.js} +85 -71
- package/dist/{chunk-11v28977.js.map → chunk-f6yme003.js.map} +6 -6
- package/dist/{chunk-4ddzsqtc.js → chunk-hg7pf34j.js} +4 -3
- package/dist/{chunk-4ddzsqtc.js.map → chunk-hg7pf34j.js.map} +5 -5
- package/dist/{chunk-w1zyhj40.js → chunk-jq4e17tj.js} +2 -2
- package/dist/{chunk-kvcep7ft.js → chunk-y63c6zcv.js} +967 -218
- package/dist/chunk-y63c6zcv.js.map +53 -0
- package/dist/cli/args.d.ts +7 -1
- package/dist/cli/bin.js +66 -25
- package/dist/cli/bin.js.map +6 -5
- package/dist/cli/manifest.d.ts +5 -1
- package/dist/cli/texture-cache.d.ts +4 -0
- package/dist/core/definition.d.ts +7 -0
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/export/credits.d.ts +4 -0
- package/dist/export/glb-options.d.ts +48 -0
- package/dist/export/glb.d.ts +5 -35
- package/dist/export/hierarchy.d.ts +23 -0
- package/dist/export/index.d.ts +2 -1
- package/dist/export/index.js +21 -7
- package/dist/export/index.js.map +1 -1
- package/dist/export/lod-coverage.d.ts +12 -0
- package/dist/export/metadata.d.ts +2 -0
- package/dist/export/version.d.ts +2 -2
- package/dist/grammar/index.js +2 -2
- package/dist/index.js +63 -17
- package/dist/index.js.map +1 -1
- package/dist/kinds/head-armour.d.ts +3 -7
- package/dist/kinds/head-bust.d.ts +3 -2
- package/dist/kinds/head-face.d.ts +3 -1
- package/dist/kinds/head-garb.d.ts +4 -0
- package/dist/kinds/head-mesh.d.ts +4 -0
- package/dist/kinds/head-mouth.d.ts +4 -0
- package/dist/kinds/head-params.d.ts +9 -1
- package/dist/kinds/head-tone.d.ts +10 -0
- package/dist/kinds/head-torso.d.ts +35 -0
- package/dist/kinds/index.js +6 -6
- package/dist/material/cc0.d.ts +4 -0
- package/dist/material/define.d.ts +34 -0
- package/dist/material/generator.d.ts +1 -1
- package/dist/material/index.d.ts +8 -3
- package/dist/material/index.js +39 -7
- package/dist/material/index.js.map +1 -1
- package/dist/material/library.d.ts +7 -0
- package/dist/material/material-def.d.ts +30 -0
- package/dist/material/paint/canvas.d.ts +7 -0
- package/dist/material/paint/default-sheet.d.ts +3 -1
- package/dist/material/paint/fabric.d.ts +4 -0
- package/dist/material/paint/image-strip.d.ts +4 -0
- package/dist/material/paint/kit.d.ts +5 -0
- package/dist/material/paint/metal.d.ts +3 -0
- package/dist/material/paint/orm.d.ts +1 -1
- package/dist/material/paint/strip.d.ts +1 -1
- package/dist/material/paint/types.d.ts +32 -2
- package/dist/material/palette.d.ts +2 -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 +20 -4
- package/dist/neural/index.js +2 -2
- package/dist/passes/index.js +2 -2
- package/dist/primitives/index.js +3 -3
- package/dist/qa/index.js +2 -2
- package/dist/render/beauty.d.ts +1 -1
- package/dist/render/index.js +3 -3
- package/dist/render/lighting.d.ts +7 -0
- package/dist/render/shade.d.ts +1 -1
- package/dist/testing/index.js +5 -5
- package/docs/README.md +4 -0
- package/docs/architecture/gltf-capabilities.md +38 -1
- package/docs/architecture/materials.md +151 -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/plans/roadmap.md +12 -0
- package/package.json +2 -1
- package/recipes/boxcar/README.md +7 -0
- package/recipes/boxcar/boxcar.ts +118 -34
- package/recipes/goblin_head/README.md +27 -14
- package/recipes/goblin_head/goblin_head.ts +11 -9
- package/recipes/human_head/README.md +8 -4
- package/recipes/human_head/human_head.ts +4 -3
- package/recipes/material_swatches/material_swatches.ts +11 -5
- package/recipes/orc_head/README.md +32 -20
- package/recipes/orc_head/orc_head.ts +9 -7
- package/src/cli/args.ts +16 -3
- package/src/cli/manifest.ts +6 -0
- package/src/cli/run.ts +33 -9
- package/src/cli/texture-cache.ts +24 -0
- package/src/core/asset.ts +1 -0
- package/src/core/definition.ts +7 -0
- package/src/core/errors.ts +6 -1
- package/src/export/credits.ts +25 -0
- package/src/export/glb-options.ts +53 -0
- package/src/export/glb.ts +100 -113
- package/src/export/hierarchy.ts +122 -0
- package/src/export/index.ts +17 -1
- package/src/export/lod-coverage.ts +71 -0
- package/src/export/metadata.ts +3 -0
- package/src/export/version.ts +1 -1
- package/src/kinds/head-armour.ts +103 -73
- package/src/kinds/head-bust.ts +49 -86
- package/src/kinds/head-face.ts +94 -74
- package/src/kinds/head-garb.ts +181 -0
- package/src/kinds/head-hair.ts +74 -33
- package/src/kinds/head-mesh.ts +43 -5
- package/src/kinds/head-mouth.ts +111 -0
- package/src/kinds/head-params.ts +11 -2
- package/src/kinds/head-presets.ts +30 -27
- package/src/kinds/head-tone.ts +52 -0
- package/src/kinds/head-torso.ts +230 -0
- package/src/kinds/head.ts +8 -3
- package/src/material/cc0.ts +41 -0
- package/src/material/define.ts +130 -0
- package/src/material/generator.ts +15 -8
- package/src/material/index.ts +34 -3
- package/src/material/library.ts +196 -0
- package/src/material/material-def.ts +84 -0
- package/src/material/paint/canvas.ts +11 -1
- package/src/material/paint/compose.ts +1 -1
- package/src/material/paint/decode-png.ts +19 -13
- package/src/material/paint/default-sheet.ts +40 -28
- package/src/material/paint/fabric.ts +63 -0
- package/src/material/paint/image-strip.ts +88 -0
- package/src/material/paint/kit.ts +37 -0
- package/src/material/paint/masks.ts +3 -1
- package/src/material/paint/metal.ts +109 -0
- package/src/material/paint/orm.ts +14 -1
- package/src/material/paint/planks.ts +49 -4
- package/src/material/paint/strip.ts +35 -29
- package/src/material/paint/types.ts +45 -2
- package/src/material/paint/validate.ts +19 -3
- package/src/material/palette.ts +3 -0
- package/src/material/slots.ts +18 -0
- package/src/material/texture-source.ts +86 -0
- package/src/material/trim-sheet.ts +79 -13
- package/src/render/beauty.ts +2 -2
- package/src/render/lighting.ts +26 -0
- package/src/render/shade.ts +10 -5
- package/dist/chunk-1ypfs7ga.js.map +0 -42
- package/dist/chunk-gcyp9b4d.js.map +0 -22
- package/dist/chunk-kgvbpqqw.js.map +0 -10
- package/dist/chunk-kvcep7ft.js.map +0 -43
- /package/dist/{chunk-a8fxakcj.js.map → chunk-6b05mnmx.js.map} +0 -0
- /package/dist/{chunk-xnt2sdhf.js.map → chunk-drna2rny.js.map} +0 -0
- /package/dist/{chunk-ravbb06q.js.map → chunk-dxvypr7y.js.map} +0 -0
- /package/dist/{chunk-w1zyhj40.js.map → chunk-jq4e17tj.js.map} +0 -0
package/src/kinds/head-armour.ts
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
// The orc's armoured bust gear as sculpt groups:
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// The orc's armoured bust gear as sculpt groups: plate pauldrons built like real armour — a domed
|
|
2
|
+
// shoulder cap and two shingled lames, each a hollow shell (so its edge shows a thickness and a
|
|
3
|
+
// shadowed underside) with a rolled rim, a raised crest and bone spikes along it, rivets — plus a
|
|
4
|
+
// leather baldric that hugs the chest (a shell of the torso ∩ a diagonal slab) with an iron buckle,
|
|
5
|
+
// and a fur mantle of clumped, tapering strands over the trapezius. Head units, mirrored across Y.
|
|
5
6
|
|
|
6
7
|
import type { Vec3 } from '../core/index.ts';
|
|
7
8
|
import type { SculptEdit } from '../primitives/index.ts';
|
|
8
9
|
import { alongZ, unit } from './head-geom.ts';
|
|
9
10
|
import type { HeadParams } from './head-params.ts';
|
|
11
|
+
import { type BustFrame, bustCut, torsoForms } from './head-torso.ts';
|
|
10
12
|
|
|
11
13
|
const RAD = Math.PI / 180;
|
|
12
14
|
const shade = (c: Vec3, k: number): Vec3 => [c[0] * k, c[1] * k, c[2] * k];
|
|
13
15
|
|
|
14
16
|
/** Half-space kept above the plane through `p` whose normal is +Z rolled `deg` about X. */
|
|
15
|
-
const above = (p: Vec3, deg: number
|
|
17
|
+
const above = (p: Vec3, deg: number): SculptEdit => {
|
|
16
18
|
const n: Vec3 = [0, -Math.sin(deg * RAD), Math.cos(deg * RAD)];
|
|
17
19
|
return {
|
|
18
20
|
shape: 'roundBox',
|
|
19
21
|
at: [p[0] + n[0], p[1] + n[1], p[2] + n[2]],
|
|
20
22
|
size: [4, 4, 2],
|
|
21
23
|
rotate: [deg, 0, 0],
|
|
22
|
-
op,
|
|
24
|
+
op: 'intersect',
|
|
23
25
|
};
|
|
24
26
|
};
|
|
25
27
|
|
|
@@ -32,84 +34,119 @@ const slab = (p: Vec3, deg: number, t: number): SculptEdit => ({
|
|
|
32
34
|
op: 'intersect',
|
|
33
35
|
});
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
at
|
|
38
|
-
|
|
37
|
+
const ell = (at: Vec3, r: Vec3, g: number, roll: number): SculptEdit => ({
|
|
38
|
+
shape: 'ellipsoid',
|
|
39
|
+
at,
|
|
40
|
+
size: [r[0] + g, r[1] + g, r[2] + g],
|
|
41
|
+
rotate: [roll, 0, 0],
|
|
39
42
|
});
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
/** One plate: a shell of thickness `t` trimmed above a tilted plane, with a rolled rim. */
|
|
45
|
+
function plate(
|
|
46
|
+
at: Vec3,
|
|
47
|
+
r: Vec3,
|
|
48
|
+
roll: number,
|
|
49
|
+
cut: Vec3,
|
|
50
|
+
tilt: number,
|
|
51
|
+
c: Vec3,
|
|
52
|
+
t: number,
|
|
53
|
+
): SculptEdit[] {
|
|
54
|
+
return [
|
|
55
|
+
{
|
|
56
|
+
group: [
|
|
57
|
+
ell(at, r, 0, roll),
|
|
58
|
+
// Hollow only while the shell is thinner than the plate itself (fine LODs).
|
|
59
|
+
...(t < Math.min(...r) * 0.6 ? [{ ...ell(at, r, -t, roll), op: 'subtract' as const }] : []),
|
|
60
|
+
above(cut, tilt),
|
|
61
|
+
],
|
|
62
|
+
color: c,
|
|
63
|
+
blend: 0.006,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
group: [
|
|
67
|
+
ell(at, r, 0.022, roll),
|
|
68
|
+
...(t < Math.min(...r) * 0.6
|
|
69
|
+
? [{ ...ell(at, r, -t * 0.8, roll), op: 'subtract' as const }]
|
|
70
|
+
: []),
|
|
71
|
+
slab(cut, tilt, 0.05),
|
|
72
|
+
],
|
|
73
|
+
// Worn edge: the rim catches the light (a painter's edge highlight).
|
|
74
|
+
color: shade(c, 1.3),
|
|
75
|
+
blend: 0.006,
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** `cell`: the bust's sculpt cell; plates thicken to ≈ 2.5 cells so far LODs stay closed. */
|
|
81
|
+
export function armour(p: HeadParams, f: BustFrame, cell = 0): SculptEdit[] {
|
|
82
|
+
const t = Math.max(0.085, cell * 2.5);
|
|
42
83
|
const c = p.colors;
|
|
84
|
+
const { cx, s, b, nz } = f;
|
|
43
85
|
const metal = c.metal;
|
|
44
86
|
const out: SculptEdit[] = [];
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
color: shade(metal, 1 - k * 0.12),
|
|
55
|
-
blend: 0.008,
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
group: [
|
|
59
|
-
{ shape: 'ellipsoid', at, size: [r[0] + 0.018, r[1] + 0.018, r[2] + 0.018] },
|
|
60
|
-
slab(cut, tilt, 0.035),
|
|
61
|
-
],
|
|
62
|
-
color: shade(metal, 0.55),
|
|
63
|
-
blend: 0.004,
|
|
64
|
-
},
|
|
65
|
-
);
|
|
66
|
-
// Rivets along the top of each lame's rim.
|
|
87
|
+
// Pauldron: a big shoulder cap, then two lames shingled out and down over the arm.
|
|
88
|
+
const cap: Vec3 = [cx - 0.04, s * 0.8, b + 0.66];
|
|
89
|
+
for (let k = 1; k >= 0; k--) {
|
|
90
|
+
const at: Vec3 = [cap[0], cap[1] + k * 0.07 * s, cap[2] - k * 0.17];
|
|
91
|
+
const r: Vec3 = k === 0 ? [0.27, 0.29, 0.25] : [0.23, 0.25, 0.19];
|
|
92
|
+
const tilt = -(38 + k * 14);
|
|
93
|
+
const cut: Vec3 = [at[0], at[1], at[2] - r[2] * 0.45];
|
|
94
|
+
out.push(...plate(at, r, -25 - k * 10, cut, tilt, shade(metal, 1 - k * 0.15), t));
|
|
95
|
+
// Rivets along each plate's rim.
|
|
67
96
|
for (let j = 0; j < 4; j++) {
|
|
68
|
-
const
|
|
97
|
+
const x = at[0] + (j - 1.5) * 0.12;
|
|
69
98
|
out.push({
|
|
70
99
|
shape: 'sphere',
|
|
71
|
-
at: [at[
|
|
72
|
-
size: 0.
|
|
73
|
-
color: shade(metal, 1.
|
|
100
|
+
at: [x, at[1] + r[1] * 0.62, cut[2] + 0.02 - r[1] * 0.62 * Math.tan(-tilt * RAD) * 0.4],
|
|
101
|
+
size: 0.017,
|
|
102
|
+
color: shade(metal, 1.35),
|
|
74
103
|
blend: 0.004,
|
|
75
104
|
});
|
|
76
105
|
}
|
|
77
106
|
}
|
|
78
|
-
//
|
|
107
|
+
// Crest along the cap's crown, bone spikes rising out of it.
|
|
108
|
+
const crest: Vec3 = [cap[0], cap[1] - 0.03, cap[2] + 0.25];
|
|
109
|
+
out.push({
|
|
110
|
+
shape: 'ellipsoid',
|
|
111
|
+
at: crest,
|
|
112
|
+
size: [0.25, 0.028, 0.07],
|
|
113
|
+
rotate: [-22, 0, 0],
|
|
114
|
+
color: shade(metal, 0.75),
|
|
115
|
+
blend: 0.02,
|
|
116
|
+
});
|
|
79
117
|
for (let k = 0; k < 3; k++)
|
|
80
118
|
out.push({
|
|
81
119
|
shape: 'cone',
|
|
82
|
-
at: [
|
|
83
|
-
size: [0.
|
|
84
|
-
rotate: alongZ(unit([(k - 1) * 0.
|
|
120
|
+
at: [crest[0] - 0.14 + k * 0.14, crest[1] + 0.01, crest[2] + 0.02 - Math.abs(k - 1) * 0.03],
|
|
121
|
+
size: [0.075, 0.012, 0.36 - Math.abs(k - 1) * 0.12],
|
|
122
|
+
rotate: alongZ(unit([(k - 1) * 0.3, 0.55, 1])),
|
|
85
123
|
color: c.teeth,
|
|
86
|
-
blend: 0.
|
|
124
|
+
blend: 0.015,
|
|
87
125
|
});
|
|
88
|
-
// Baldric: a shell of the chest cut to a diagonal band
|
|
89
|
-
const
|
|
90
|
-
const grow = (g: number): Vec3 => [chest.r[0] + g, chest.r[1] + g, chest.r[2] + g];
|
|
91
|
-
const band: Vec3 = [cx, 0, b + 0.28];
|
|
126
|
+
// Baldric: a shell of the chest cut to a diagonal band over the left shoulder; iron buckle.
|
|
127
|
+
const band: Vec3 = [cx, 0, b + 0.36];
|
|
92
128
|
out.push(
|
|
93
129
|
{
|
|
94
|
-
group: [{
|
|
130
|
+
group: [{ group: torsoForms(f, 0.03) }, slab(band, 50, 0.15), bustCut(f)],
|
|
95
131
|
color: c.leather,
|
|
96
132
|
blend: 0.006,
|
|
97
133
|
mirror: false,
|
|
98
134
|
},
|
|
99
135
|
{
|
|
100
136
|
group: [
|
|
101
|
-
{
|
|
137
|
+
{ group: torsoForms(f, 0.055) },
|
|
138
|
+
bustCut(f),
|
|
102
139
|
{
|
|
103
140
|
shape: 'roundBox',
|
|
104
|
-
at: [cx + 0.3, 0.02, b + 0.
|
|
105
|
-
size: [0.3, 0.
|
|
106
|
-
rotate: [
|
|
141
|
+
at: [cx + 0.3, 0.02, b + 0.38],
|
|
142
|
+
size: [0.3, 0.14, 0.14],
|
|
143
|
+
rotate: [50, 0, 0],
|
|
107
144
|
op: 'intersect',
|
|
108
145
|
},
|
|
109
146
|
{
|
|
110
147
|
shape: 'ellipsoid',
|
|
111
|
-
at: [cx + 0.42, 0.02, b + 0.
|
|
112
|
-
size: [0.1, 0.
|
|
148
|
+
at: [cx + 0.42, 0.02, b + 0.38],
|
|
149
|
+
size: [0.1, 0.04, 0.04],
|
|
113
150
|
op: 'subtract',
|
|
114
151
|
},
|
|
115
152
|
],
|
|
@@ -118,33 +155,26 @@ export function armour(p: HeadParams, cx: number, s: number, b: number, nz: numb
|
|
|
118
155
|
mirror: false,
|
|
119
156
|
},
|
|
120
157
|
);
|
|
121
|
-
// Fur mantle
|
|
158
|
+
// Fur mantle over the trapezius: clumped strands fanning back and out from behind the neck.
|
|
122
159
|
const fur = c.fur ?? c.leather;
|
|
123
|
-
const R =
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
color: shade(fur, 0.75),
|
|
129
|
-
blend: 0.04,
|
|
130
|
-
});
|
|
131
|
-
for (let row = 0; row < 2; row++)
|
|
132
|
-
for (let i = 0; i < 10; i++) {
|
|
133
|
-
const a = (-85 + i * 20 + row * 10) * RAD;
|
|
134
|
-
const rr = R + row * 0.05;
|
|
160
|
+
const R = f.n + 0.08;
|
|
161
|
+
for (let row = 0; row < 3; row++)
|
|
162
|
+
for (let i = 0; i < 8; i++) {
|
|
163
|
+
const a = (-80 + i * 22 + row * 11) * RAD;
|
|
164
|
+
const rr = R + row * 0.07;
|
|
135
165
|
const root: Vec3 = [
|
|
136
|
-
cx - 0.
|
|
137
|
-
Math.abs(Math.sin(a)) * rr + 0.
|
|
138
|
-
nz + 0.
|
|
166
|
+
cx - 0.1 - Math.abs(Math.cos(a)) * rr * 0.9,
|
|
167
|
+
Math.abs(Math.sin(a)) * rr + 0.02,
|
|
168
|
+
nz + 0.16 - row * 0.05,
|
|
139
169
|
];
|
|
140
|
-
const dir = unit([
|
|
170
|
+
const dir = unit([-0.7, Math.abs(Math.sin(a)) * 0.8, -0.55 - ((i + row) % 2) * 0.3]);
|
|
141
171
|
out.push({
|
|
142
172
|
shape: 'cone',
|
|
143
173
|
at: root,
|
|
144
|
-
size: [0.
|
|
174
|
+
size: [0.075 - row * 0.008, 0.012, 0.16 + ((i + row) % 3) * 0.04],
|
|
145
175
|
rotate: alongZ(dir),
|
|
146
|
-
color: (i + row) % 2 ? fur : shade(fur, 0.
|
|
147
|
-
blend: 0.
|
|
176
|
+
color: (i + row) % 2 ? fur : shade(fur, 0.72),
|
|
177
|
+
blend: 0.035,
|
|
148
178
|
});
|
|
149
179
|
}
|
|
150
180
|
return out;
|
package/src/kinds/head-bust.ts
CHANGED
|
@@ -1,38 +1,22 @@
|
|
|
1
|
-
// The bust under the head:
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// Head units, mirrored across Y; the neck itself belongs to the head.
|
|
1
|
+
// The bust under the head: the anatomy, bust cut and plinth (head-torso.ts) plus the gear that
|
|
2
|
+
// sells the race at a glance — spiked plate pauldrons and a fur mantle (orc, head-armour.ts), a
|
|
3
|
+
// leather vest, scarf and cowl (goblin, head-garb.ts), a draped hood, or a tunic with mantle and
|
|
4
|
+
// brooch (human). Head units, mirrored across Y; the neck itself belongs to the head.
|
|
5
5
|
|
|
6
6
|
import type { Vec3 } from '../core/index.ts';
|
|
7
|
-
import type
|
|
8
|
-
import { armour
|
|
7
|
+
import { type SculptEdit, sculptField } from '../primitives/index.ts';
|
|
8
|
+
import { armour } from './head-armour.ts';
|
|
9
|
+
import { vest } from './head-garb.ts';
|
|
9
10
|
import type { HeadLayout } from './head-layout.ts';
|
|
10
11
|
import type { HeadParams } from './head-params.ts';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
size: [0.16, 0.19, 0.12],
|
|
20
|
-
rotate: [0, 0, 12],
|
|
21
|
-
color: c.skin,
|
|
22
|
-
blend: 0.07,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
shape: 'capsule',
|
|
26
|
-
at: [cx + 0.33, 0, b + 0.42],
|
|
27
|
-
to: [cx + 0.34, 0, b + 0.1],
|
|
28
|
-
size: 0.012,
|
|
29
|
-
op: 'subtract',
|
|
30
|
-
blend: 0.04,
|
|
31
|
-
color: c.shade,
|
|
32
|
-
mirror: false,
|
|
33
|
-
},
|
|
34
|
-
];
|
|
35
|
-
}
|
|
12
|
+
import {
|
|
13
|
+
type BustFrame,
|
|
14
|
+
bodyEdits,
|
|
15
|
+
bustCut,
|
|
16
|
+
frameOf,
|
|
17
|
+
plinthEdits,
|
|
18
|
+
torsoForms,
|
|
19
|
+
} from './head-torso.ts';
|
|
36
20
|
|
|
37
21
|
function hood(p: HeadParams, cx: number, nz: number): SculptEdit[] {
|
|
38
22
|
const c = p.colors;
|
|
@@ -80,14 +64,16 @@ function hood(p: HeadParams, cx: number, nz: number): SculptEdit[] {
|
|
|
80
64
|
];
|
|
81
65
|
}
|
|
82
66
|
|
|
83
|
-
function tunic(p: HeadParams,
|
|
67
|
+
function tunic(p: HeadParams, f: BustFrame): SculptEdit[] {
|
|
84
68
|
const c = p.colors;
|
|
85
|
-
const
|
|
86
|
-
const shell = (g: number): SculptEdit => ({
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
69
|
+
const { cx, s, b, nz } = f;
|
|
70
|
+
const shell = (g: number): SculptEdit => ({ group: torsoForms(f, g) });
|
|
71
|
+
const torso = sculptField(torsoForms(f), { color: c.cloth, mirror: 'y' }).field;
|
|
72
|
+
const front = (z: number): number => {
|
|
73
|
+
let x = cx + 0.7;
|
|
74
|
+
for (let i = 0; i < 300 && torso([x, 0, z]) > 0; i++) x -= 0.004;
|
|
75
|
+
return x;
|
|
76
|
+
};
|
|
91
77
|
const n = p.neck.radius;
|
|
92
78
|
const axis = (z: number): Vec3 => [cx - 0.08, 0, z];
|
|
93
79
|
const mantle = (g: number): SculptEdit => ({
|
|
@@ -117,6 +103,7 @@ function tunic(p: HeadParams, cx: number, s: number, b: number, nz: number): Scu
|
|
|
117
103
|
mantle(0),
|
|
118
104
|
{ shape: 'roundBox', at: [0, 0, b + 0.3 + 1], size: [4, 4, 2], op: 'intersect' },
|
|
119
105
|
opening,
|
|
106
|
+
bustCut(f),
|
|
120
107
|
],
|
|
121
108
|
color: c.cape ?? c.leather,
|
|
122
109
|
blend: 0.01,
|
|
@@ -154,68 +141,44 @@ function tunic(p: HeadParams, cx: number, s: number, b: number, nz: number): Scu
|
|
|
154
141
|
blend: 0.004,
|
|
155
142
|
mirror: false,
|
|
156
143
|
},
|
|
157
|
-
|
|
144
|
+
// Brass buttons seated on the chest surface (probed along −X at the midline).
|
|
145
|
+
...[0.5, 0.38, 0.26].map(
|
|
158
146
|
(z): SculptEdit => ({
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
],
|
|
147
|
+
shape: 'sphere',
|
|
148
|
+
at: [front(b + z) + 0.014, 0, b + z],
|
|
149
|
+
size: 0.028,
|
|
163
150
|
color: c.metal,
|
|
164
|
-
blend: 0.
|
|
151
|
+
blend: 0.004,
|
|
165
152
|
mirror: false,
|
|
166
153
|
}),
|
|
167
154
|
),
|
|
168
155
|
];
|
|
169
156
|
}
|
|
170
157
|
|
|
171
|
-
|
|
158
|
+
/** `cell`: the bust's sculpt cell (head units), floors the plinth disc so far LODs keep it. */
|
|
159
|
+
export function bustEdits(p: HeadParams, l: HeadLayout, cell = 0): SculptEdit[] {
|
|
172
160
|
const c = p.colors;
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
const nz = b + 0.6; // neck root
|
|
177
|
-
const body = p.bust.style === 'pauldrons' ? c.skin : c.cloth;
|
|
161
|
+
const f = frameOf(p, l);
|
|
162
|
+
const style = p.bust.style;
|
|
163
|
+
const bare = style === 'pauldrons' || style === 'vest';
|
|
178
164
|
const e: SculptEdit[] = [
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
at: [cx - 0.16, 0.1, nz + 0.16],
|
|
183
|
-
to: [cx - 0.1, s * 0.78, b + 0.44],
|
|
184
|
-
size: 0.15,
|
|
185
|
-
color: body,
|
|
186
|
-
blend: 0.15,
|
|
187
|
-
},
|
|
188
|
-
{ shape: 'sphere', at: [cx - 0.04, s * 0.84, b + 0.3], size: 0.2, color: body, blend: 0.1 },
|
|
189
|
-
// Trapezius: from high behind the neck down to the shoulder.
|
|
190
|
-
{
|
|
191
|
-
shape: 'capsule',
|
|
192
|
-
at: [cx - 0.2, 0.08, nz + 0.3],
|
|
193
|
-
to: [cx - 0.1, s * 0.62, b + 0.5],
|
|
194
|
-
size: 0.1 + p.neck.radius * 0.2,
|
|
195
|
-
color: body,
|
|
196
|
-
blend: 0.12,
|
|
197
|
-
},
|
|
165
|
+
...bodyEdits(p, f, bare ? c.skin : c.cloth),
|
|
166
|
+
// Neck root in skin, so the head's neck seats into the bust without a cloth ring (flattened,
|
|
167
|
+
// so it never pokes through the chest below the collar).
|
|
198
168
|
{
|
|
199
|
-
shape: '
|
|
200
|
-
at: [cx - 0.
|
|
201
|
-
|
|
202
|
-
size: p.neck.radius * 1.05,
|
|
169
|
+
shape: 'ellipsoid',
|
|
170
|
+
at: [f.cx - 0.09, 0, f.nz + 0.06],
|
|
171
|
+
size: [f.n * 1.02, f.n * 1.02, 0.12],
|
|
203
172
|
color: c.skin,
|
|
204
|
-
blend: 0.
|
|
173
|
+
blend: 0.04,
|
|
205
174
|
},
|
|
206
175
|
];
|
|
207
|
-
|
|
208
|
-
e.push(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
op: 'subtract',
|
|
213
|
-
color: body,
|
|
214
|
-
});
|
|
215
|
-
if (p.bust.style === 'pauldrons') e.push(...torso(p, cx, b), ...armour(p, cx, s, b, nz));
|
|
216
|
-
else if (p.bust.style === 'hood') e.push(...hood(p, cx, nz));
|
|
217
|
-
else e.push(...tunic(p, cx, s, b, nz));
|
|
176
|
+
if (style === 'pauldrons') e.push(...armour(p, f, cell));
|
|
177
|
+
else if (style === 'hood') e.push(...hood(p, f.cx, f.nz));
|
|
178
|
+
else if (style === 'vest') e.push(...vest(p, f));
|
|
179
|
+
else e.push(...tunic(p, f));
|
|
180
|
+
e.push(...plinthEdits(p, f, cell * 1.5));
|
|
218
181
|
// Base cut flat at z = base.
|
|
219
|
-
e.push({ shape: 'roundBox', at: [0, 0,
|
|
182
|
+
e.push({ shape: 'roundBox', at: [0, 0, f.floor - 1], size: [4, 4, 2], op: 'subtract' });
|
|
220
183
|
return e;
|
|
221
184
|
}
|