@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/recipes/boxcar/boxcar.ts
CHANGED
|
@@ -15,9 +15,11 @@ import {
|
|
|
15
15
|
lathe,
|
|
16
16
|
type Mesh,
|
|
17
17
|
merge,
|
|
18
|
+
NATURAL,
|
|
18
19
|
noOverlap,
|
|
19
20
|
place,
|
|
20
21
|
prism,
|
|
22
|
+
type StripId,
|
|
21
23
|
symmetric,
|
|
22
24
|
text,
|
|
23
25
|
tiled,
|
|
@@ -46,10 +48,10 @@ const DOOR_Z0 = 1.15;
|
|
|
46
48
|
const DOOR_Z1 = 3.64;
|
|
47
49
|
const JOURNAL_Y = 0.925; // axle end = journal box inner face
|
|
48
50
|
|
|
49
|
-
const steel = hex('#
|
|
50
|
-
const iron = hex('#
|
|
51
|
-
const wheelC = hex('#
|
|
52
|
-
const walk =
|
|
51
|
+
const steel = hex('#a59f97'); // tints iron_cast: dark, sand-cast trucks and gear
|
|
52
|
+
const iron = hex('#a8a29c'); // tints iron_rust: weathered hardware
|
|
53
|
+
const wheelC = hex('#948f8a');
|
|
54
|
+
const walk = NATURAL; // wood_weathered carries its own silver-grey
|
|
53
55
|
|
|
54
56
|
interface Params {
|
|
55
57
|
/** Large road name left of the door. */
|
|
@@ -71,7 +73,7 @@ function bx(
|
|
|
71
73
|
z0: number,
|
|
72
74
|
z1: number,
|
|
73
75
|
color: Vec3,
|
|
74
|
-
strip:
|
|
76
|
+
strip: StripId = 'iron_cast',
|
|
75
77
|
bevel = 0,
|
|
76
78
|
): Mesh {
|
|
77
79
|
const [a, b] = [Math.min(x0, x1), Math.max(x0, x1)];
|
|
@@ -88,7 +90,7 @@ function bx(
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
const rod = (from: Vec3, to: Vec3, w: number, color: Vec3 = iron, segments = 0): Mesh =>
|
|
91
|
-
beam({ from, to, width: w, color, strip: '
|
|
93
|
+
beam({ from, to, width: w, color, strip: 'iron_rust', ...(segments ? { segments } : {}) });
|
|
92
94
|
|
|
93
95
|
// ---------------------------------------------------------------- running gear
|
|
94
96
|
/** One wheel's tread disc (exactly 33 in) at lateral y, outer face toward ±y. */
|
|
@@ -101,6 +103,7 @@ function tread(lod: Lod, y: number): Mesh {
|
|
|
101
103
|
axis: 'y',
|
|
102
104
|
at: [0, y, 0],
|
|
103
105
|
color: wheelC,
|
|
106
|
+
strip: 'iron_cast',
|
|
104
107
|
});
|
|
105
108
|
const s = Math.sign(y);
|
|
106
109
|
const segs = [24, 14, 8][lod] ?? 8;
|
|
@@ -114,7 +117,7 @@ function tread(lod: Lod, y: number): Mesh {
|
|
|
114
117
|
segments: segs,
|
|
115
118
|
axis: 'y',
|
|
116
119
|
color: wheelC,
|
|
117
|
-
strip: '
|
|
120
|
+
strip: 'iron_cast',
|
|
118
121
|
});
|
|
119
122
|
}
|
|
120
123
|
|
|
@@ -130,6 +133,7 @@ function wheelset(lod: Lod): Mesh {
|
|
|
130
133
|
axis: 'y',
|
|
131
134
|
at: [0, s * (WHEEL_Y + TREAD_W / 2 + 0.025), 0],
|
|
132
135
|
color: wheelC,
|
|
136
|
+
strip: 'iron_cast',
|
|
133
137
|
});
|
|
134
138
|
const shaft = cylinder({
|
|
135
139
|
radius: 0.075,
|
|
@@ -137,6 +141,7 @@ function wheelset(lod: Lod): Mesh {
|
|
|
137
141
|
segments: 4,
|
|
138
142
|
axis: 'y',
|
|
139
143
|
color: wheelC,
|
|
144
|
+
strip: 'iron_cast',
|
|
140
145
|
});
|
|
141
146
|
return merge([shaft, hub(1), hub(-1)]);
|
|
142
147
|
}
|
|
@@ -148,6 +153,7 @@ function wheelset(lod: Lod): Mesh {
|
|
|
148
153
|
segments: Math.max(6, segs / 2),
|
|
149
154
|
axis: 'y',
|
|
150
155
|
color: wheelC,
|
|
156
|
+
strip: 'iron_cast',
|
|
151
157
|
}),
|
|
152
158
|
];
|
|
153
159
|
for (const s of [1, -1]) {
|
|
@@ -163,6 +169,7 @@ function wheelset(lod: Lod): Mesh {
|
|
|
163
169
|
segments: segs,
|
|
164
170
|
axis: 'y',
|
|
165
171
|
color: wheelC,
|
|
172
|
+
strip: 'iron_cast',
|
|
166
173
|
caps: false,
|
|
167
174
|
}),
|
|
168
175
|
);
|
|
@@ -177,6 +184,7 @@ function wheelset(lod: Lod): Mesh {
|
|
|
177
184
|
axis: 'y',
|
|
178
185
|
at: [0, yo + s * 0.02, 0],
|
|
179
186
|
color: wheelC,
|
|
187
|
+
strip: 'iron_cast',
|
|
180
188
|
}),
|
|
181
189
|
);
|
|
182
190
|
for (let k = 0; k < 6; k++) {
|
|
@@ -232,7 +240,7 @@ function truckFrame(lod: Lod, bevel: number): Mesh {
|
|
|
232
240
|
r - 0.09,
|
|
233
241
|
r + 0.12,
|
|
234
242
|
steel,
|
|
235
|
-
'
|
|
243
|
+
'iron_cast',
|
|
236
244
|
bevel,
|
|
237
245
|
),
|
|
238
246
|
);
|
|
@@ -250,7 +258,17 @@ function truckFrame(lod: Lod, bevel: number): Mesh {
|
|
|
250
258
|
); // lid
|
|
251
259
|
}
|
|
252
260
|
out.push(
|
|
253
|
-
bx(
|
|
261
|
+
bx(
|
|
262
|
+
ax[0] - 0.16,
|
|
263
|
+
ax[1] + 0.16,
|
|
264
|
+
y - 0.06,
|
|
265
|
+
y + 0.06,
|
|
266
|
+
r + 0.12,
|
|
267
|
+
zt + 0.07,
|
|
268
|
+
steel,
|
|
269
|
+
'iron_cast',
|
|
270
|
+
bevel,
|
|
271
|
+
),
|
|
254
272
|
); // top member
|
|
255
273
|
if (lod < 2) {
|
|
256
274
|
const p: [number, number][] = [
|
|
@@ -275,6 +293,7 @@ function truckFrame(lod: Lod, bevel: number): Mesh {
|
|
|
275
293
|
segments: coilSegs,
|
|
276
294
|
at: [d, y, zb + 0.125],
|
|
277
295
|
color: iron,
|
|
296
|
+
strip: 'iron_rust',
|
|
278
297
|
}),
|
|
279
298
|
);
|
|
280
299
|
if (lod === 0)
|
|
@@ -286,6 +305,7 @@ function truckFrame(lod: Lod, bevel: number): Mesh {
|
|
|
286
305
|
segments: coilSegs,
|
|
287
306
|
at: [d, y, zb + 0.06 + k * 0.042],
|
|
288
307
|
color: iron,
|
|
308
|
+
strip: 'iron_rust',
|
|
289
309
|
}),
|
|
290
310
|
);
|
|
291
311
|
}
|
|
@@ -298,7 +318,17 @@ function truckFrame(lod: Lod, bevel: number): Mesh {
|
|
|
298
318
|
}
|
|
299
319
|
// Bolster on the spring nests, centre plate up to the car's body bolster.
|
|
300
320
|
out.push(
|
|
301
|
-
bx(
|
|
321
|
+
bx(
|
|
322
|
+
-0.15,
|
|
323
|
+
0.15,
|
|
324
|
+
-(JOURNAL_Y + 0.12),
|
|
325
|
+
JOURNAL_Y + 0.12,
|
|
326
|
+
zb + 0.21,
|
|
327
|
+
0.6,
|
|
328
|
+
steel,
|
|
329
|
+
'iron_cast',
|
|
330
|
+
bevel,
|
|
331
|
+
),
|
|
302
332
|
);
|
|
303
333
|
out.push(
|
|
304
334
|
cylinder({
|
|
@@ -307,6 +337,7 @@ function truckFrame(lod: Lod, bevel: number): Mesh {
|
|
|
307
337
|
segments: [12, 8, 6][lod] ?? 6,
|
|
308
338
|
at: [0, 0, 0.62],
|
|
309
339
|
color: steel,
|
|
340
|
+
strip: 'iron_cast',
|
|
310
341
|
}),
|
|
311
342
|
);
|
|
312
343
|
if (lod < 2)
|
|
@@ -454,15 +485,29 @@ const usra = defineFamily({
|
|
|
454
485
|
|
|
455
486
|
// Underframe: straight centre sill, side sills, end sills, body bolsters, crossbearers, floor.
|
|
456
487
|
const uf: Mesh[] = [
|
|
457
|
-
bx(-XB - 0.04, XB + 0.04, -0.2, 0.2, 0.72, 1.1, steel, '
|
|
458
|
-
bx(-XB, XB, -SHEATH_Y + 0.02, SHEATH_Y - 0.02, 1.1, FLOOR_Z, walk, '
|
|
488
|
+
bx(-XB - 0.04, XB + 0.04, -0.2, 0.2, 0.72, 1.1, steel, 'iron_cast', bevel),
|
|
489
|
+
bx(-XB, XB, -SHEATH_Y + 0.02, SHEATH_Y - 0.02, 1.1, FLOOR_Z, walk, 'wood_weathered'),
|
|
459
490
|
];
|
|
460
491
|
for (const s of [-1, 1])
|
|
461
|
-
uf.push(
|
|
492
|
+
uf.push(
|
|
493
|
+
bx(-XB, XB, s * SHEATH_Y, s * (FRAME_Y - 0.01), 0.95, 1.18, steel, 'iron_cast', bevel),
|
|
494
|
+
);
|
|
462
495
|
for (const d of [-1, 1]) {
|
|
463
|
-
uf.push(bx(d * XB, d * (XB + 0.05), -FRAME_Y, FRAME_Y, 0.74, 1.18, steel, 'iron', bevel)); // end sill / striker
|
|
464
496
|
uf.push(
|
|
465
|
-
bx(d *
|
|
497
|
+
bx(d * XB, d * (XB + 0.05), -FRAME_Y, FRAME_Y, 0.74, 1.18, steel, 'iron_cast', bevel),
|
|
498
|
+
); // end sill / striker
|
|
499
|
+
uf.push(
|
|
500
|
+
bx(
|
|
501
|
+
d * (TC / 2) - 0.2,
|
|
502
|
+
d * (TC / 2) + 0.2,
|
|
503
|
+
-1.25,
|
|
504
|
+
1.25,
|
|
505
|
+
0.64,
|
|
506
|
+
1.1,
|
|
507
|
+
steel,
|
|
508
|
+
'iron_cast',
|
|
509
|
+
bevel,
|
|
510
|
+
),
|
|
466
511
|
); // body bolster
|
|
467
512
|
if (lod < 2)
|
|
468
513
|
for (const x of [1.6, 3.0])
|
|
@@ -482,7 +527,7 @@ const usra = defineFamily({
|
|
|
482
527
|
u: [0, 0, EAVE_Z - boardsZ0],
|
|
483
528
|
v,
|
|
484
529
|
color: paint,
|
|
485
|
-
strip: lod === 2 ? 'plain' : '
|
|
530
|
+
strip: lod === 2 ? 'plain' : 'wood_painted',
|
|
486
531
|
vMetres: lod === 2 ? BODY_L : 0.6,
|
|
487
532
|
}),
|
|
488
533
|
);
|
|
@@ -501,7 +546,9 @@ const usra = defineFamily({
|
|
|
501
546
|
);
|
|
502
547
|
}
|
|
503
548
|
for (const d of [-1, 1])
|
|
504
|
-
bodyM.push(
|
|
549
|
+
bodyM.push(
|
|
550
|
+
bx(d * (XB - 0.05), d * XB, -SHEATH_Y, SHEATH_Y, 1.0, EAVE_Z, paint, 'steel_painted'),
|
|
551
|
+
);
|
|
505
552
|
ctx.part('body', merge(bodyM));
|
|
506
553
|
|
|
507
554
|
// Outside steel framing: posts, diagonal braces leaning toward the door, top plate, end ribs.
|
|
@@ -515,11 +562,20 @@ const usra = defineFamily({
|
|
|
515
562
|
for (const d of [-1, 1]) {
|
|
516
563
|
for (const px of lod === 2 ? [] : posts) {
|
|
517
564
|
const x = d * px;
|
|
518
|
-
fr.push(bx(x - postW / 2, x + postW / 2, y0, y1, 1.0, EAVE_Z, paint, '
|
|
565
|
+
fr.push(bx(x - postW / 2, x + postW / 2, y0, y1, 1.0, EAVE_Z, paint, 'steel_painted'));
|
|
519
566
|
}
|
|
520
567
|
// Door posts.
|
|
521
568
|
fr.push(
|
|
522
|
-
bx(
|
|
569
|
+
bx(
|
|
570
|
+
d * (DOOR_W / 2 + 0.01),
|
|
571
|
+
d * (DOOR_W / 2 + 0.1),
|
|
572
|
+
y0,
|
|
573
|
+
y1,
|
|
574
|
+
1.0,
|
|
575
|
+
EAVE_Z,
|
|
576
|
+
paint,
|
|
577
|
+
'steel_painted',
|
|
578
|
+
),
|
|
523
579
|
);
|
|
524
580
|
if (lod < 2)
|
|
525
581
|
for (let i = 0; i < 4; i++) {
|
|
@@ -533,13 +589,15 @@ const usra = defineFamily({
|
|
|
533
589
|
width: 0.075,
|
|
534
590
|
height: 0.06,
|
|
535
591
|
color: paint,
|
|
536
|
-
strip: '
|
|
592
|
+
strip: 'steel_painted',
|
|
537
593
|
up: [0, s, 0],
|
|
538
594
|
}),
|
|
539
595
|
);
|
|
540
596
|
}
|
|
541
597
|
}
|
|
542
|
-
fr.push(
|
|
598
|
+
fr.push(
|
|
599
|
+
bx(-XB, XB, y0, s * (FRAME_Y + 0.01), EAVE_Z - 0.16, EAVE_Z, paint, 'steel_painted', bevel),
|
|
600
|
+
); // side plate
|
|
543
601
|
if (lod === 0) {
|
|
544
602
|
// Rivet heads where braces meet posts and sills.
|
|
545
603
|
const rivet = cylinder({
|
|
@@ -569,7 +627,7 @@ const usra = defineFamily({
|
|
|
569
627
|
1.0,
|
|
570
628
|
EAVE_Z,
|
|
571
629
|
paint,
|
|
572
|
-
'
|
|
630
|
+
'steel_painted',
|
|
573
631
|
),
|
|
574
632
|
);
|
|
575
633
|
if (lod < 2)
|
|
@@ -584,7 +642,7 @@ const usra = defineFamily({
|
|
|
584
642
|
z - 0.05,
|
|
585
643
|
z + 0.05,
|
|
586
644
|
paint,
|
|
587
|
-
'
|
|
645
|
+
'steel_painted',
|
|
588
646
|
),
|
|
589
647
|
);
|
|
590
648
|
}
|
|
@@ -606,7 +664,7 @@ const usra = defineFamily({
|
|
|
606
664
|
depth: BODY_L + 0.1,
|
|
607
665
|
plane: 'yz',
|
|
608
666
|
color: paintDark,
|
|
609
|
-
strip: '
|
|
667
|
+
strip: 'steel_painted',
|
|
610
668
|
}),
|
|
611
669
|
];
|
|
612
670
|
if (lod === 0) {
|
|
@@ -627,12 +685,14 @@ const usra = defineFamily({
|
|
|
627
685
|
ctx.part('roof', merge(roofM));
|
|
628
686
|
|
|
629
687
|
const walkM: Mesh[] = [
|
|
630
|
-
bx(-XB - 0.12, XB + 0.12, -0.255, 0.255, WALK_TOP - 0.045, WALK_TOP, walk, '
|
|
688
|
+
bx(-XB - 0.12, XB + 0.12, -0.255, 0.255, WALK_TOP - 0.045, WALK_TOP, walk, 'wood_weathered'),
|
|
631
689
|
];
|
|
632
690
|
const saddles = lod === 2 ? 3 : 11;
|
|
633
691
|
for (let i = 0; i < saddles; i++) {
|
|
634
692
|
const x = -XB + 0.3 + ((BODY_L - 0.6) * i) / (saddles - 1);
|
|
635
|
-
walkM.push(
|
|
693
|
+
walkM.push(
|
|
694
|
+
bx(x - 0.05, x + 0.05, -0.2, 0.2, RIDGE_Z - 0.03, WALK_TOP - 0.045, walk, 'wood_weathered'),
|
|
695
|
+
);
|
|
636
696
|
}
|
|
637
697
|
if (lod < 2)
|
|
638
698
|
for (const d of [-1, 1]) {
|
|
@@ -647,7 +707,7 @@ const usra = defineFamily({
|
|
|
647
707
|
WALK_TOP - 0.045,
|
|
648
708
|
WALK_TOP,
|
|
649
709
|
walk,
|
|
650
|
-
'
|
|
710
|
+
'wood_weathered',
|
|
651
711
|
),
|
|
652
712
|
);
|
|
653
713
|
walkM.push(
|
|
@@ -659,7 +719,7 @@ const usra = defineFamily({
|
|
|
659
719
|
EAVE_Z,
|
|
660
720
|
WALK_TOP - 0.045,
|
|
661
721
|
walk,
|
|
662
|
-
'
|
|
722
|
+
'wood_weathered',
|
|
663
723
|
),
|
|
664
724
|
);
|
|
665
725
|
}
|
|
@@ -679,15 +739,26 @@ const usra = defineFamily({
|
|
|
679
739
|
u: [0, 0, DOOR_Z1 - DOOR_Z0],
|
|
680
740
|
v: [s > 0 ? -DOOR_W : DOOR_W, 0, 0],
|
|
681
741
|
color: paint,
|
|
682
|
-
strip: lod === 2 ? 'plain' : '
|
|
742
|
+
strip: lod === 2 ? 'plain' : 'wood_painted',
|
|
683
743
|
}),
|
|
684
|
-
bx(
|
|
744
|
+
bx(
|
|
745
|
+
-DOOR_W / 2,
|
|
746
|
+
DOOR_W / 2,
|
|
747
|
+
y0,
|
|
748
|
+
y1 - s * 0.003,
|
|
749
|
+
DOOR_Z0,
|
|
750
|
+
DOOR_Z1,
|
|
751
|
+
paintDark,
|
|
752
|
+
'wood_painted',
|
|
753
|
+
),
|
|
685
754
|
];
|
|
686
755
|
if (lod < 2) {
|
|
687
756
|
for (const z of [DOOR_Z0, (DOOR_Z0 + DOOR_Z1) / 2 - 0.05, DOOR_Z1 - 0.1])
|
|
688
|
-
dm.push(
|
|
757
|
+
dm.push(
|
|
758
|
+
bx(-DOOR_W / 2, DOOR_W / 2, y1, y1 + s * 0.015, z, z + 0.1, paint, 'steel_painted'),
|
|
759
|
+
); // steel straps
|
|
689
760
|
for (const x of [-DOOR_W / 2, DOOR_W / 2 - 0.08])
|
|
690
|
-
dm.push(bx(x, x + 0.08, y1, y1 + s * 0.015, DOOR_Z0, DOOR_Z1, paint, '
|
|
761
|
+
dm.push(bx(x, x + 0.08, y1, y1 + s * 0.015, DOOR_Z0, DOOR_Z1, paint, 'steel_painted'));
|
|
691
762
|
}
|
|
692
763
|
if (lod === 0) dm.push(bx(-0.75, -0.65, y1 + s * 0.015, y1 + s * 0.06, 2.0, 2.2, iron)); // handle / latch
|
|
693
764
|
ctx.part(`door_${side}`, dirt(merge(dm)), {
|
|
@@ -733,10 +804,20 @@ const usra = defineFamily({
|
|
|
733
804
|
COUPLER_Z - 0.07,
|
|
734
805
|
COUPLER_Z + 0.07,
|
|
735
806
|
iron,
|
|
736
|
-
'
|
|
807
|
+
'iron_rust',
|
|
808
|
+
bevel,
|
|
809
|
+
),
|
|
810
|
+
bx(
|
|
811
|
+
d * 6.5,
|
|
812
|
+
d * 6.7,
|
|
813
|
+
-0.17,
|
|
814
|
+
0.12,
|
|
815
|
+
COUPLER_Z - 0.13,
|
|
816
|
+
COUPLER_Z + 0.13,
|
|
817
|
+
iron,
|
|
818
|
+
'iron_rust',
|
|
737
819
|
bevel,
|
|
738
820
|
),
|
|
739
|
-
bx(d * 6.5, d * 6.7, -0.17, 0.12, COUPLER_Z - 0.13, COUPLER_Z + 0.13, iron, 'iron', bevel),
|
|
740
821
|
];
|
|
741
822
|
if (lod < 2) cm.push(bx(d * 6.6, d * 6.7, 0.12, 0.2, COUPLER_Z - 0.1, COUPLER_Z + 0.1, iron)); // knuckle
|
|
742
823
|
if (lod === 0) {
|
|
@@ -810,6 +891,7 @@ const usra = defineFamily({
|
|
|
810
891
|
],
|
|
811
892
|
segments: wheelSegs,
|
|
812
893
|
color: iron,
|
|
894
|
+
strip: 'iron_rust',
|
|
813
895
|
at: [-(XB + 0.13), -0.85, 0],
|
|
814
896
|
}),
|
|
815
897
|
);
|
|
@@ -836,6 +918,7 @@ const usra = defineFamily({
|
|
|
836
918
|
axis: 'x',
|
|
837
919
|
at: [-0.9, 0.55, 0.9],
|
|
838
920
|
color: iron,
|
|
921
|
+
strip: 'iron_rust',
|
|
839
922
|
}),
|
|
840
923
|
cylinder({
|
|
841
924
|
radius: 0.16,
|
|
@@ -844,6 +927,7 @@ const usra = defineFamily({
|
|
|
844
927
|
axis: 'x',
|
|
845
928
|
at: [0.4, -0.5, 0.92],
|
|
846
929
|
color: iron,
|
|
930
|
+
strip: 'iron_rust',
|
|
847
931
|
}),
|
|
848
932
|
bx(-0.25, -0.05, 0.2, 0.42, 0.85, 1.1, iron),
|
|
849
933
|
rod([-0.35, 0.55, 1.08], [-0.35, 0.2, 1.08], 0.05, iron),
|
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## Identity
|
|
4
4
|
A sly, scrawny goblin tinker, read from the 3/4 view by: **huge leaf-shaped ears** wider than its
|
|
5
|
-
shoulders, a **long
|
|
6
|
-
a
|
|
5
|
+
shoulders, a **long hooked nose** (one form drawn out to a drooping point, nostrils tucked at its
|
|
6
|
+
base), a **wide grin** of small pointed teeth with the cheeks bunched up beside it, **big amber eyes
|
|
7
|
+
under heavy upper lids**, a **worried creased brow**, a **sharp little chin**, **pale wispy hair**
|
|
8
|
+
straggling off a warty dome, a **skinny corded neck** on **narrow bony shoulders** in a linen shirt,
|
|
9
|
+
leather vest, strap and buckle, a **knotted scarf** and a **hood worn down** on the back — cut as a
|
|
10
|
+
collectible bust on a turned plinth.
|
|
7
11
|
|
|
8
12
|
## Design spec (head units: 1 = chin to crown = 0.19 m, ≈ 0.8 × human)
|
|
9
13
|
| Feature | Value (units) | Metres | Note |
|
|
10
14
|
|---|---|---|---|
|
|
11
15
|
| chin → crown | 1.00 | 0.190 | the `height` param |
|
|
12
|
-
| overall bust L × W × H | — | 0.
|
|
16
|
+
| overall bust L × W × H | — | 0.227 × 0.342 × 0.372 | `real`, ±5 %; width is the ears |
|
|
13
17
|
| cranium width / depth / above eye line | 0.66 / 0.84 / 0.50 | 0.125 / 0.160 / 0.095 | |
|
|
14
|
-
| eyeball radius / spacing | 0.
|
|
15
|
-
| ears length / width / sweep / lift | 0.
|
|
16
|
-
| nose length / bridge / hook | 0.20 / 0.
|
|
17
|
-
| mouth half-width / grin | 0.
|
|
18
|
-
|
|
|
18
|
+
| eyeball radius / spacing / upper lid | 0.08 / 0.155 / 30° | 0.015 / pupils 0.059 apart | eyes 1.5 × human relative size, heavy lids |
|
|
19
|
+
| ears length / width / sweep / lift | 0.66 / 0.30 / 24° / 6° | 0.125 long | two tapered blades + hollow, near level |
|
|
20
|
+
| nose length / bridge / hook / tip | 0.20 / 0.13 / 0.065 / 0.046 | | bridge cone + hump + tip + drooping point cone |
|
|
21
|
+
| mouth half-width / grin | 0.21 / 0.80 | | crescent cut, 3 + 3 pointed teeth a side, tongue |
|
|
22
|
+
| forehead creases / chin | 3 / 0.72 | | creases painted (thinner than a cell) |
|
|
23
|
+
| neck radius / shoulders / mass | 0.14 / 0.52 / 0 | 0.027 / 0.099 | bony: clavicles under the shirt |
|
|
19
24
|
|
|
20
25
|
## Palette (sRGB)
|
|
21
26
|
| Role | Hex |
|
|
@@ -23,8 +28,9 @@ a few **wiry hair tufts** and warts on a bald dome, and a **draped hood** on nar
|
|
|
23
28
|
| skin / shade / lips | `#9cb84a` / `#6a8a2c` / `#7a8e36` |
|
|
24
29
|
| eyes: sclera / iris | `#f0e6a0` / `#e0a020` |
|
|
25
30
|
| teeth / mouth | `#e8dcae` / `#3a1010` |
|
|
26
|
-
| hair
|
|
27
|
-
|
|
|
31
|
+
| wispy hair | `#c8c0b0` |
|
|
32
|
+
| linen shirt / vest + strap (×0.65) / scarf / hood / buckle | `#b8a47c` / `#6b4428` / `#9a3a24` / `#4e5e3a` / `#a88a4a` |
|
|
33
|
+
| plinth / cut faces | `#6f6c6c` (linear 0.16, 0.15, 0.15) / ×1.3 |
|
|
28
34
|
|
|
29
35
|
## Parts the engine needs
|
|
30
36
|
| Part | Pivot | Motion |
|
|
@@ -39,8 +45,11 @@ along the lips, the marionette lines and under the chin. Its cut faces are paint
|
|
|
39
45
|
when it opens; it laps 0.75 cells over the head so no gap shows.
|
|
40
46
|
|
|
41
47
|
## LODs
|
|
42
|
-
Sculpt cell = 2 % / 4.5 % / 14 % of head height. LOD0/1 bake vertex AO across all
|
|
43
|
-
|
|
48
|
+
Sculpt cell = 2 % / 4.5 % / 14 % of head height (bust 1.6 ×). LOD0/1 bake vertex AO across all
|
|
49
|
+
parts (16/10 rays); every LOD carries the painted tone. LOD1/2 carve the grin as a plain wide line
|
|
50
|
+
(no teeth or tongue: a crescent that thin folds over at coarse cells); LOD2 drops warts, hair and
|
|
51
|
+
the AO bake, floors ear thickness and the nose's radii at about a cell, and makes the plinth one
|
|
52
|
+
drum so the bust stays on the ground.
|
|
44
53
|
|
|
45
54
|
## References (stylised anatomy, not likeness)
|
|
46
55
|
| Topic | Link | Used for |
|
|
@@ -48,6 +57,9 @@ warts, wiry tufts and the AO bake, and floors ear thickness at 0.7 cells so ears
|
|
|
48
57
|
| Loomis head: eye line at half height, brow/nose/chin thirds | https://finearttutorials.com/guide/loomis-method/ | human eye line 0.5, nose base 0.32, mouth 0.22 |
|
|
49
58
|
| Proportions of the head (ears between brow and nose base, head ≈ 5 eyes wide) | https://artyfactory.com/portraits/pencil-portraits/proportions-of-a-head.html | ear placement, eye size vs head width |
|
|
50
59
|
| Primary → secondary → tertiary forms (block brow/cheek/jaw first, then cut lids and folds) | https://www.creativebloq.com/how-to/how-to-sculpt-and-pose-a-cartoon-head-in-zbrush | the edit order in `head-face.ts` |
|
|
60
|
+
| Goblin from a base head: enlarge the top of the head, elongate the ears, broaden their base | https://create.roblox.com/docs/art/characters/creating/sculpting | cranium and ear roots |
|
|
61
|
+
| Goblin sculpt breakdown: big nose, cracked grin, ears, sparse hair | https://www.foxrenderfarm.com/share/how-to-create-goblin-in-zbrush | feature priorities |
|
|
62
|
+
| Goblin features: large pointed ears, mischievous grin with visible teeth | https://sketchok.com/other/monsters-and-fantasy-creatures/how-to-draw-a-goblin/ | grin + teeth |
|
|
51
63
|
| Orc: widen head and jawline to break human proportion | https://www.creativebloq.com/3d/how-create-orc-zbrush-61411834 | orc jaw width 0.37 vs human 0.22 |
|
|
52
64
|
| Stylised orc head study (silhouette, form language) | https://blenderartists.org/t/orc-head-study-stylized-characters/1655542 | massing check |
|
|
53
65
|
| Stylised hair: large clumps first, hairline → crown, one primary flow direction | https://gamesartist.co.uk/curly-hair-ls/ · https://www.cgchannel.com/2023/03/tutorial-creating-stylized-hairstyles-using-zbrush | `swept` locks in head-hair.ts |
|
|
@@ -58,5 +70,6 @@ hand-painted stylised fantasy" is the idiom (exaggerated primary forms, generous
|
|
|
58
70
|
vertex colour with baked AO), not a specific source.
|
|
59
71
|
|
|
60
72
|
## Known gaps
|
|
61
|
-
Teeth
|
|
62
|
-
|
|
73
|
+
Teeth are small at this scale and read best in the 3/4 view; the wisps are cone pairs, not hair
|
|
74
|
+
cards; the jaw part's seam shows as a faint line at the mouth corners up close; garment edges are
|
|
75
|
+
vertex-colour soft and the strap/buckle are simple shells.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Stylised goblin bust — an original design in the chunky hand-painted fantasy idiom: huge leaf
|
|
2
|
-
// ears, long
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// ears, a long hooked nose, a wide grin of small pointed teeth, big amber eyes under heavy lids, a
|
|
3
|
+
// worried creased brow, pale wispy hair, warts; a skinny neck and bony shoulders in a linen shirt,
|
|
4
|
+
// leather vest, strap, knotted scarf and a hood worn down, cut as a collectible bust on a turned
|
|
5
|
+
// plinth. The `head` kind sculpts it (race preset `goblin`, chin to crown 0.19 m); this file is the
|
|
6
|
+
// spec. Design dossier: recipes/goblin_head/README.md.
|
|
5
7
|
|
|
6
8
|
import { defineAsset, head } from '@developerz.ai/ts23d';
|
|
7
9
|
|
|
@@ -9,22 +11,22 @@ export const goblin_head = defineAsset({
|
|
|
9
11
|
name: 'goblin_head',
|
|
10
12
|
kind: 'character',
|
|
11
13
|
description:
|
|
12
|
-
'Stylised goblin bust: huge pointed ears, long
|
|
14
|
+
'Stylised goblin bust on a plinth: huge pointed ears, long hooked nose, toothy grin, heavy-lidded amber eyes, wispy hair, leather vest and scarf',
|
|
13
15
|
tags: ['character', 'fantasy', 'goblin', 'bust', 'sculpt'],
|
|
14
16
|
license: 'CC0-1.0',
|
|
15
17
|
sources: ['recipes/goblin_head/README.md (design dossier: proportions, palette, references)'],
|
|
16
18
|
// Design spec, not a real-world one: overall bust size and eye spacing from the dossier.
|
|
17
19
|
real: {
|
|
18
|
-
length: 0.
|
|
19
|
-
width: 0.
|
|
20
|
-
height: 0.
|
|
20
|
+
length: 0.227,
|
|
21
|
+
width: 0.342,
|
|
22
|
+
height: 0.372,
|
|
21
23
|
measures: [
|
|
22
24
|
{
|
|
23
25
|
label: 'eye spacing (pupil to pupil)',
|
|
24
|
-
value: 0.
|
|
26
|
+
value: 0.0589,
|
|
25
27
|
axis: 'y',
|
|
26
28
|
between: ['eye_l', 'eye_r'],
|
|
27
|
-
source: '0.
|
|
29
|
+
source: '0.155 × 2 × 0.19 m',
|
|
28
30
|
},
|
|
29
31
|
],
|
|
30
32
|
},
|
|
@@ -5,13 +5,14 @@ A young adventurer, read from the 3/4 view by: **classical proportions pushed sl
|
|
|
5
5
|
(square jaw, strong brow, thick neck), **defined arched eyebrows**, **swept-back hair** in chunky
|
|
6
6
|
tapered two-tone locks over a rounded hairline, a **clipped beard with chin volume and a drooping
|
|
7
7
|
moustache**, clear blue eyes, a **blue tunic** with a standing leather collar, placket and brass
|
|
8
|
-
buttons, and a **red mantle** over the shoulders pinned with a brass brooch
|
|
8
|
+
buttons, and a **red mantle** over the shoulders pinned with a brass brooch — cut as a collectible
|
|
9
|
+
bust (U-cut under the chest) on a turned plinth.
|
|
9
10
|
|
|
10
11
|
## Design spec (head units: 1 = chin to crown = 0.23 m, adult human)
|
|
11
12
|
| Feature | Value (units) | Metres | Note |
|
|
12
13
|
|---|---|---|---|
|
|
13
14
|
| chin → crown | 1.00 | 0.230 | adult menton–vertex ≈ 0.22–0.24 m |
|
|
14
|
-
| overall bust L × W × H | — | 0.247 × 0.
|
|
15
|
+
| overall bust L × W × H | — | 0.247 × 0.428 × 0.439 | `real`, ±5 % |
|
|
15
16
|
| cranium width / depth | 0.66 / 0.86 | 0.152 / 0.198 | adult head breadth ≈ 0.15 m, length ≈ 0.19–0.20 m |
|
|
16
17
|
| eye line | 0.50 | — | Loomis: half height |
|
|
17
18
|
| eyeball radius / spacing | 0.052 / 0.135 | 0.012 / pupils 0.062 apart | human eyeball ≈ 24 mm; IPD ≈ 62 mm |
|
|
@@ -19,6 +20,7 @@ buttons, and a **red mantle** over the shoulders pinned with a brass brooch.
|
|
|
19
20
|
| jaw half-width | 0.22 | 0.051 | bigonial ≈ 0.10 m |
|
|
20
21
|
| ears | round, 0.27 long | 0.062 | between brow and nose base |
|
|
21
22
|
| neck radius | 0.27 | 0.062 | heroic: thicker than average |
|
|
23
|
+
| shoulders / mass | 0.95 / 0.5 (default) | 0.219 | pectorals, deltoids, trapezius under the tunic |
|
|
22
24
|
|
|
23
25
|
## Palette (sRGB)
|
|
24
26
|
| Role | Hex |
|
|
@@ -27,6 +29,7 @@ buttons, and a **red mantle** over the shoulders pinned with a brass brooch.
|
|
|
27
29
|
| eyes: sclera / iris | `#ece6dc` / `#3d6e9e` |
|
|
28
30
|
| hair + beard (two-tone locks ×1.5) | `#5a3a22` |
|
|
29
31
|
| tunic / mantle / collar, placket / brooch, buttons | `#2e4f8a` / `#7a2420` / `#6b4428` / `#c9a24a` |
|
|
32
|
+
| plinth / cut faces | `#6f6c6c` (linear 0.16, 0.15, 0.15) / ×1.3 |
|
|
30
33
|
|
|
31
34
|
## Parts the engine needs
|
|
32
35
|
| Part | Pivot | Motion |
|
|
@@ -41,8 +44,9 @@ along the lips, the marionette lines and under the chin. Its cut faces are paint
|
|
|
41
44
|
when it opens; it laps 0.75 cells over the head so no gap shows.
|
|
42
45
|
|
|
43
46
|
## LODs
|
|
44
|
-
Sculpt cell = 2 % / 4.5 % / 14 % of head height. LOD0/1 bake vertex AO across all
|
|
45
|
-
|
|
47
|
+
Sculpt cell = 2 % / 4.5 % / 14 % of head height (bust 1.6 ×). LOD0/1 bake vertex AO across all
|
|
48
|
+
parts (16/10 rays); every LOD carries the painted tone (cavity, warm top, cool under). LOD2 drops
|
|
49
|
+
the AO bake, floors ear thickness at 0.7 cells and makes the plinth one drum.
|
|
46
50
|
|
|
47
51
|
## References (stylised anatomy, not likeness)
|
|
48
52
|
| Topic | Link | Used for |
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Stylised human hero bust — an original design in the chunky hand-painted fantasy idiom:
|
|
2
2
|
// classical proportions (eye line at half height, brow/nose/chin thirds) pushed slightly heroic —
|
|
3
3
|
// square jaw, strong brow — with short swept hair, a short beard and moustache, a blue tunic,
|
|
4
|
-
// leather collar and
|
|
5
|
-
// 0.23 m); this file is the spec. Design dossier:
|
|
4
|
+
// leather collar and mantle, cut as a collectible bust on a turned plinth. The `head` kind sculpts
|
|
5
|
+
// it (race preset `human`, chin to crown 0.23 m); this file is the spec. Design dossier:
|
|
6
|
+
// recipes/human_head/README.md.
|
|
6
7
|
|
|
7
8
|
import { defineAsset, head } from '@developerz.ai/ts23d';
|
|
8
9
|
|
|
@@ -17,7 +18,7 @@ export const human_head = defineAsset({
|
|
|
17
18
|
// Design spec, not a real-world one: overall bust size and eye spacing from the dossier.
|
|
18
19
|
real: {
|
|
19
20
|
length: 0.247,
|
|
20
|
-
width: 0.
|
|
21
|
+
width: 0.428,
|
|
21
22
|
height: 0.439,
|
|
22
23
|
measures: [
|
|
23
24
|
{
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type Mesh,
|
|
20
20
|
MeshBuilder,
|
|
21
21
|
merge,
|
|
22
|
+
NATURAL,
|
|
22
23
|
STRIPS,
|
|
23
24
|
type StripId,
|
|
24
25
|
type Vec3,
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
|
|
27
28
|
const STONE = hex('#9c968c');
|
|
28
29
|
const INK = hex('#1c1c1c');
|
|
29
|
-
const TINT: Readonly<Record<StripId, Vec3
|
|
30
|
+
const TINT: Readonly<Partial<Record<StripId, Vec3>>> = {
|
|
30
31
|
plain: hex('#e8e4dc'),
|
|
31
32
|
brick: hex('#9a4a32'),
|
|
32
33
|
stone: hex('#c8bba0'),
|
|
@@ -128,7 +129,8 @@ export const material_swatches = defineAsset({
|
|
|
128
129
|
kind: 'prop',
|
|
129
130
|
description:
|
|
130
131
|
'Every trim-sheet strip and cell on labelled panels: the material generator test card.',
|
|
131
|
-
|
|
132
|
+
// 24 strip panels (library v2) + decal and foliage cells.
|
|
133
|
+
budget: { triangles: [24000, 1200, 800] },
|
|
132
134
|
build(ctx) {
|
|
133
135
|
const stripSpan = STRIPS.length * 2 + (STRIPS.length - 1) * FRONT.gap;
|
|
134
136
|
const cells: readonly CellId[] = [...DECAL_CELLS, ...FOLIAGE_CELLS];
|
|
@@ -151,7 +153,7 @@ export const material_swatches = defineAsset({
|
|
|
151
153
|
const panel = box({
|
|
152
154
|
size: [0.06, 2, h],
|
|
153
155
|
at: [FRONT.x, y, FRONT.height],
|
|
154
|
-
color: TINT[id],
|
|
156
|
+
color: TINT[id] ?? NATURAL,
|
|
155
157
|
strip: id,
|
|
156
158
|
});
|
|
157
159
|
ctx.part(`strip_${id}`, panel, id === 'glass' ? { surface: 'glass' } : {});
|
|
@@ -167,10 +169,14 @@ export const material_swatches = defineAsset({
|
|
|
167
169
|
surface: 'cutout',
|
|
168
170
|
});
|
|
169
171
|
} else {
|
|
170
|
-
const back = box({
|
|
172
|
+
const back = box({
|
|
173
|
+
size: [0.06, 1, 1],
|
|
174
|
+
at: [REAR.x, y, REAR.height],
|
|
175
|
+
color: TINT.plain ?? NATURAL,
|
|
176
|
+
});
|
|
171
177
|
ctx.part(
|
|
172
178
|
`cell_${id}`,
|
|
173
|
-
merge([back, cellQuad(id, REAR.x + 0.032, y, REAR.height, 1, TINT.plain)]),
|
|
179
|
+
merge([back, cellQuad(id, REAR.x + 0.032, y, REAR.height, 1, TINT.plain ?? NATURAL)]),
|
|
174
180
|
);
|
|
175
181
|
}
|
|
176
182
|
if (ctx.lod === 0)
|