wobnimo 0.1.4 → 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/AGENTS.md +18 -4
- package/README.md +38 -7
- package/dist/characters/bao.d.ts +6 -0
- package/dist/characters/bao.js +1 -0
- package/dist/characters/berry.d.ts +6 -0
- package/dist/characters/berry.js +1 -0
- package/dist/characters/boo.js +1 -1
- package/dist/characters/bumble.d.ts +6 -0
- package/dist/characters/bumble.js +1 -0
- package/dist/characters/cacti.d.ts +6 -0
- package/dist/characters/cacti.js +1 -0
- package/dist/characters/cap.d.ts +6 -0
- package/dist/characters/cap.js +1 -0
- package/dist/characters/drift.d.ts +6 -0
- package/dist/characters/drift.js +1 -0
- package/dist/characters/ember.d.ts +6 -0
- package/dist/characters/ember.js +1 -0
- package/dist/characters/hop.d.ts +6 -0
- package/dist/characters/hop.js +1 -0
- package/dist/characters/index.d.ts +11 -1
- package/dist/characters/index.js +11 -11
- package/dist/characters/inky.js +1 -1
- package/dist/characters/latte.d.ts +6 -0
- package/dist/characters/latte.js +1 -0
- package/dist/characters/microduck.js +1 -1
- package/dist/characters/miso.js +1 -1
- package/dist/characters/mochi.js +1 -1
- package/dist/characters/pebble.js +1 -1
- package/dist/characters/pip.js +1 -1
- package/dist/characters/rex.js +1 -1
- package/dist/characters/swirl.d.ts +6 -0
- package/dist/characters/swirl.js +1 -0
- package/dist/characters/tad.js +1 -1
- package/dist/characters/volt.js +1 -1
- package/dist/cli.js +1888 -0
- package/dist/element.d.ts +1 -1
- package/dist/element.js +9 -9
- package/dist/index-Cm5wWKoT.d.ts +12 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -6
- package/dist/react.js +16 -16
- package/dist/wobnimo.js +12 -12
- package/package.json +14 -4
- package/skill/SKILL.md +719 -0
- package/skill/examples/bao.ts +73 -0
- package/skill/examples/berry.ts +97 -0
- package/skill/examples/boo.ts +87 -0
- package/skill/examples/bumble.ts +70 -0
- package/skill/examples/cacti.ts +79 -0
- package/skill/examples/cap.ts +59 -0
- package/skill/examples/drift.ts +74 -0
- package/skill/examples/ember.ts +78 -0
- package/skill/examples/hop.ts +66 -0
- package/skill/examples/inky.ts +168 -0
- package/skill/examples/latte.ts +73 -0
- package/skill/examples/microduck.ts +222 -0
- package/skill/examples/miso.ts +182 -0
- package/skill/examples/mochi.ts +78 -0
- package/skill/examples/pebble.ts +174 -0
- package/skill/examples/pip.ts +170 -0
- package/skill/examples/rex.ts +193 -0
- package/skill/examples/swirl.ts +69 -0
- package/skill/examples/tad.ts +117 -0
- package/skill/examples/volt.ts +214 -0
- package/skill/reference/recipes.md +551 -0
- package/skill/reference/spec.md +258 -0
- package/skill/templates/starter.ts +73 -0
- package/dist/index-DRDEWSGV.d.ts +0 -8
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
# Recipes
|
|
2
|
+
|
|
3
|
+
Ready-made features. Each recipe is a fragment of a spec: merge each list
|
|
4
|
+
into the list of the same name in your file (`bones` into `bones`, `parts`
|
|
5
|
+
into `parts`, and so on), keeping the order shown.
|
|
6
|
+
|
|
7
|
+
**Every number here fits the starter template** (`npx wobnimo new`):
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
body bone pivot [0, 0, 0] body ellipsoid at [0, 0.19, 0] radii [0.25, 0.19, 0.23]
|
|
11
|
+
head bone pivot [0, 0.3, 0] head ellipsoid at [0, 0.2, 0] radii [0.31, 0.27, 0.28]
|
|
12
|
+
(head space: top of head y 0.47, front z 0.28, sides x ±0.31)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If your body or head is a different size or in a different place, move the
|
|
16
|
+
recipe with it: shift `at`, `from`, `to` and `pivot` by the same amount your
|
|
17
|
+
head or body moved, and scale sizes by how much bigger or smaller it is.
|
|
18
|
+
Then run `npx wobnimo check` and apply its fixes.
|
|
19
|
+
|
|
20
|
+
Materials named `skin` in the recipes mean your main material. Recipes that
|
|
21
|
+
add a material give its full preset.
|
|
22
|
+
|
|
23
|
+
## Finding a point on a surface
|
|
24
|
+
|
|
25
|
+
You often need a point **on** the surface: where an ear sits, where a spot
|
|
26
|
+
goes, where a paint belongs. For an ellipsoid with centre `c` and radii
|
|
27
|
+
`[rx, ry, rz]`, pick a direction `d = [dx, dy, dz]` from the centre (it does
|
|
28
|
+
not need to be unit length) and compute:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
k = 1 / sqrt((dx/rx)² + (dy/ry)² + (dz/rz)²)
|
|
32
|
+
point = [c.x + dx·k, c.y + dy·k, c.z + dz·k]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For a sphere of radius `r`, `k = r / sqrt(dx² + dy² + dz²)`.
|
|
36
|
+
|
|
37
|
+
Example: a spot on the starter head, up and to the right and forward,
|
|
38
|
+
direction `[0.5, 0.5, 0.7]`:
|
|
39
|
+
`k = 1 / sqrt((0.5/0.31)² + (0.5/0.27)² + (0.7/0.28)²) = 1 / sqrt(2.60 + 3.43 + 6.25) = 0.285`
|
|
40
|
+
→ point `[0.143, 0.2 + 0.143, 0.2] = [0.143, 0.343, 0.2]` in head space.
|
|
41
|
+
|
|
42
|
+
To push something **into** the surface (an ear base, a horn root), move it a
|
|
43
|
+
little toward the centre. To sit **on** the surface (a paint, a spot), use the
|
|
44
|
+
point itself.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Ears
|
|
49
|
+
|
|
50
|
+
### Round ears (bear, panda, mouse, hamster)
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
materials: {
|
|
54
|
+
ear: { color: '#ffb35c', shadeOf: 'skin', f0: 0.04, spec: 0.8, shininess: 60, rim: 0.8, sss: 0.2, sssColor: '#ffd9a8' },
|
|
55
|
+
},
|
|
56
|
+
parts: [
|
|
57
|
+
{ bone: 'head', shape: 'sphere', at: [-0.2, 0.42, -0.03], radius: 0.09, blend: 0.03, material: 'ear' },
|
|
58
|
+
{ bone: 'head', shape: 'sphere', at: [0.2, 0.42, -0.03], radius: 0.09, blend: 0.03, material: 'ear' },
|
|
59
|
+
],
|
|
60
|
+
paints: [
|
|
61
|
+
/* inner ear: painted only on the 'ear' material, so it cannot spill onto the head */
|
|
62
|
+
{ bone: 'head', on: ['ear'], kind: 'sphere', at: [-0.21, 0.44, 0.055], radius: 0.05, color: '#ff9f9f', soft: 0.4 },
|
|
63
|
+
{ bone: 'head', on: ['ear'], kind: 'sphere', at: [0.21, 0.44, 0.055], radius: 0.05, color: '#ff9f9f', soft: 0.4 },
|
|
64
|
+
],
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Mouse: radius 0.13, at `[±0.24, 0.42, -0.04]`. Panda: give the ears a dark
|
|
68
|
+
material instead of the inner paint.
|
|
69
|
+
|
|
70
|
+
### Pointed ears that spring (cat, fox, wolf)
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
materials: {
|
|
74
|
+
ear: { color: '#ffb35c', shadeOf: 'skin', f0: 0.04, spec: 0.8, shininess: 60, rim: 0.8, sss: 0.2, sssColor: '#ffd9a8' },
|
|
75
|
+
},
|
|
76
|
+
bones: [
|
|
77
|
+
{ name: 'earL', parent: 'head', pivot: [-0.15, 0.4, 0], rest: { rz: 0.3 }, jiggle: { axis: [0, 1, 0], length: 0.16, freq: 3.5, damping: 0.2, maxAngle: 0.4 } },
|
|
78
|
+
{ name: 'earR', parent: 'head', pivot: [0.15, 0.4, 0], rest: { rz: -0.3 }, jiggle: { axis: [0, 1, 0], length: 0.16, freq: 3.5, damping: 0.2, maxAngle: 0.4 } },
|
|
79
|
+
],
|
|
80
|
+
parts: [
|
|
81
|
+
{ bone: 'earL', shape: 'roundCone', from: [0, 0, 0], to: [0, 0.18, 0], r1: 0.08, r2: 0.015, material: 'ear' },
|
|
82
|
+
{ bone: 'earR', shape: 'roundCone', from: [0, 0, 0], to: [0, 0.18, 0], r1: 0.08, r2: 0.015, material: 'ear' },
|
|
83
|
+
],
|
|
84
|
+
paints: [
|
|
85
|
+
{ bone: 'earL', on: ['ear'], kind: 'ellipse', at: [0, 0.07, 0.04], radii: [0.035, 0.07], color: '#ffd9c2', soft: 0.4 },
|
|
86
|
+
{ bone: 'earR', on: ['ear'], kind: 'ellipse', at: [0, 0.07, 0.04], radii: [0.035, 0.07], color: '#ffd9c2', soft: 0.4 },
|
|
87
|
+
],
|
|
88
|
+
acting: {
|
|
89
|
+
listening: { earL: { rx: -0.3 }, earR: { rx: -0.3 } },
|
|
90
|
+
sad: { earL: { rz: 1, rx: 0.4 }, earR: { rz: -1, rx: 0.4 } },
|
|
91
|
+
error: { earL: { rz: 1.1 }, earR: { rz: -1.1 } },
|
|
92
|
+
},
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Dark ear tips (fox): add a sphere paint at the tip,
|
|
96
|
+
`{ bone: 'earL', on: ['ear'], kind: 'sphere', at: [0, 0.2, 0], radius: 0.07, color: '#3a2318', soft: 0.3 }`.
|
|
97
|
+
|
|
98
|
+
### Long floppy ears (bunny)
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
materials: {
|
|
102
|
+
ear: { color: '#fbf8f4', shadeOf: 'skin', f0: 0.03, spec: 0.4, shininess: 20, rim: 1.2, sheen: 0.7, sss: 0.35, sssColor: '#ffc4d4' },
|
|
103
|
+
},
|
|
104
|
+
bones: [
|
|
105
|
+
{ name: 'earL', parent: 'head', pivot: [-0.11, 0.42, -0.02], rest: { rz: 0.3, rx: 0.15 }, jiggle: { axis: [0, 1, 0], length: 0.32, freq: 2, damping: 0.14, maxAngle: 0.8 } },
|
|
106
|
+
{ name: 'earR', parent: 'head', pivot: [0.11, 0.42, -0.02], rest: { rz: -0.3, rx: 0.15 }, jiggle: { axis: [0, 1, 0], length: 0.32, freq: 2, damping: 0.14, maxAngle: 0.8 } },
|
|
107
|
+
],
|
|
108
|
+
parts: [
|
|
109
|
+
{ bone: 'earL', shape: 'ellipsoid', at: [0, 0.2, 0], radii: [0.065, 0.22, 0.035], material: 'ear' },
|
|
110
|
+
{ bone: 'earR', shape: 'ellipsoid', at: [0, 0.2, 0], radii: [0.065, 0.22, 0.035], material: 'ear' },
|
|
111
|
+
],
|
|
112
|
+
paints: [
|
|
113
|
+
{ bone: 'earL', on: ['ear'], kind: 'ellipse', at: [0, 0.2, 0.01], radii: [0.035, 0.16], color: '#ffc2d3', soft: 0.4 },
|
|
114
|
+
{ bone: 'earR', on: ['ear'], kind: 'ellipse', at: [0, 0.2, 0.01], radii: [0.035, 0.16], color: '#ffc2d3', soft: 0.4 },
|
|
115
|
+
],
|
|
116
|
+
acting: {
|
|
117
|
+
listening: { earL: { rz: 0, rx: -0.2 }, earR: { rz: 0, rx: -0.2 } },
|
|
118
|
+
sleeping: { earL: { rz: 1.2, rx: 0.5 }, earR: { rz: -1.2, rx: 0.5 } },
|
|
119
|
+
sad: { earL: { rz: 1.4, rx: 0.6 }, earR: { rz: -1.4, rx: 0.6 } },
|
|
120
|
+
},
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
These ears make the character about 0.4 taller: run the check and copy the
|
|
124
|
+
new `frame` and `bound` it suggests.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Arms, legs and feet
|
|
129
|
+
|
|
130
|
+
### Stubby arms that wave (driven by `flap`)
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
bones: [
|
|
134
|
+
{ name: 'armL', parent: 'body', pivot: [-0.21, 0.27, 0.04], rest: { rz: -0.35 }, drive: { rz: [['flap', -1.2]] } },
|
|
135
|
+
{ name: 'armR', parent: 'body', pivot: [0.21, 0.27, 0.04], rest: { rz: 0.35 }, drive: { rz: [['flap', 1.2]] } },
|
|
136
|
+
],
|
|
137
|
+
parts: [
|
|
138
|
+
{ bone: 'armL', shape: 'capsule', from: [0, 0, 0], to: [0, -0.12, 0.02], radius: 0.05, blend: 0.03, material: 'skin' },
|
|
139
|
+
{ bone: 'armR', shape: 'capsule', from: [0, 0, 0], to: [0, -0.12, 0.02], radius: 0.05, blend: 0.03, material: 'skin' },
|
|
140
|
+
],
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The arm hangs down from the shoulder pivot. The engine raises both arms when
|
|
144
|
+
celebrating and surprised. Acting for arms (the right arm takes the opposite
|
|
145
|
+
sign of the left):
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
acting: {
|
|
149
|
+
/* a wave with the right arm: rz > 0 swings a right arm out and up */
|
|
150
|
+
celebrating: { armR: { rz: [1.8, 0.4, 3, 0] } },
|
|
151
|
+
/* droop: swing both hands in against the body */
|
|
152
|
+
sad: { armL: { rz: 0.3 }, armR: { rz: -0.3 } },
|
|
153
|
+
sleeping: { armL: { rz: 0.3 }, armR: { rz: -0.3 } },
|
|
154
|
+
},
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Feet (already in the starter)
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
parts: [
|
|
161
|
+
{ bone: 'body', shape: 'ellipsoid', at: [-0.15, 0.04, 0.08], radii: [0.1, 0.05, 0.12], blend: 0.04, material: 'foot' },
|
|
162
|
+
{ bone: 'body', shape: 'ellipsoid', at: [0.15, 0.04, 0.08], radii: [0.1, 0.05, 0.12], blend: 0.04, material: 'foot' },
|
|
163
|
+
],
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Feet sit with their bottom on the floor: `at.y` equals the y radius.
|
|
167
|
+
|
|
168
|
+
### Short legs with feet
|
|
169
|
+
|
|
170
|
+
Raise the body first: change the body ellipsoid's `at` to `[0, 0.32, 0]` and
|
|
171
|
+
the head bone's pivot to `[0, 0.43, 0]`, and remove the starter's two feet.
|
|
172
|
+
Then:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
parts: [
|
|
176
|
+
{ bone: 'body', shape: 'capsule', from: [-0.11, 0.22, 0], to: [-0.12, 0.07, 0.02], radius: 0.055, blend: 0.04, material: 'skin' },
|
|
177
|
+
{ bone: 'body', shape: 'capsule', from: [0.11, 0.22, 0], to: [0.12, 0.07, 0.02], radius: 0.055, blend: 0.04, material: 'skin' },
|
|
178
|
+
{ bone: 'body', shape: 'ellipsoid', at: [-0.12, 0.04, 0.05], radii: [0.08, 0.045, 0.11], blend: 0.03, material: 'foot' },
|
|
179
|
+
{ bone: 'body', shape: 'ellipsoid', at: [0.12, 0.04, 0.05], radii: [0.08, 0.045, 0.11], blend: 0.03, material: 'foot' },
|
|
180
|
+
],
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Wings, tails, horns and antennae
|
|
186
|
+
|
|
187
|
+
### Small wings (bird, bee, bat, angel)
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
materials: {
|
|
191
|
+
wing: { color: '#e8f6ff', f0: 0.1, spec: 1.8, shininess: 200, rim: 1.4, sss: 0.6, sssColor: '#ffffff', gloss: 0.9 },
|
|
192
|
+
},
|
|
193
|
+
bones: [
|
|
194
|
+
{ name: 'wingL', parent: 'body', pivot: [-0.16, 0.3, -0.16], drive: { rz: [['flap', -1.4]] }, wave: { rz: [0.08, 0.8, 0] } },
|
|
195
|
+
{ name: 'wingR', parent: 'body', pivot: [0.16, 0.3, -0.16], drive: { rz: [['flap', 1.4]] }, wave: { rz: [-0.08, 0.8, 1.6] } },
|
|
196
|
+
],
|
|
197
|
+
parts: [
|
|
198
|
+
{ bone: 'wingL', shape: 'ellipsoid', at: [-0.1, 0.1, 0], rot: [0, 0, 0.5], radii: [0.12, 0.17, 0.014], material: 'wing' },
|
|
199
|
+
{ bone: 'wingR', shape: 'ellipsoid', at: [0.1, 0.1, 0], rot: [0, 0, -0.5], radii: [0.12, 0.17, 0.014], material: 'wing' },
|
|
200
|
+
],
|
|
201
|
+
acting: {
|
|
202
|
+
celebrating: { wingL: { rz: [0, 0.5, 9, 0] }, wingR: { rz: [0, 0.5, 9, Math.PI] } },
|
|
203
|
+
},
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Feathered wings (bird): use the body's feather material, make them thicker
|
|
207
|
+
(`radii: [0.1, 0.16, 0.05]`) and hang them at the sides like arms.
|
|
208
|
+
|
|
209
|
+
### A tail that wags
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
bones: [
|
|
213
|
+
{ name: 'tail', parent: 'body', pivot: [0, 0.14, -0.2], rest: { rx: -0.5 }, wave: { ry: [0.25, 1.2, 0] },
|
|
214
|
+
jiggle: { axis: [0, 0, -1], length: 0.2, freq: 2.5, damping: 0.15 } },
|
|
215
|
+
],
|
|
216
|
+
parts: [
|
|
217
|
+
{ bone: 'tail', shape: 'roundCone', from: [0, 0, 0], to: [0, 0.04, -0.22], r1: 0.06, r2: 0.035, blend: 0.03, material: 'skin' },
|
|
218
|
+
],
|
|
219
|
+
acting: {
|
|
220
|
+
celebrating: { tail: { ry: [0, 0.7, 4, 0] } },
|
|
221
|
+
happy: { tail: { ry: [0, 0.7, 5, 0] } },
|
|
222
|
+
sad: { tail: { rx: 0.6 } },
|
|
223
|
+
},
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Pom-pom tail (bunny, bear): a `sphere` of radius 0.07 at `[0, 0, -0.03]` on the
|
|
227
|
+
tail bone instead. Big fluffy tail (fox, squirrel): see `ember`.
|
|
228
|
+
|
|
229
|
+
### Horns
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
materials: {
|
|
233
|
+
horn: { color: '#fff1d6', f0: 0.05, spec: 1, shininess: 80, rim: 0.4, gloss: 0.6 },
|
|
234
|
+
},
|
|
235
|
+
parts: [
|
|
236
|
+
{ bone: 'head', shape: 'roundCone', from: [-0.13, 0.4, 0], to: [-0.18, 0.56, -0.02], r1: 0.045, r2: 0.012, blend: 0.02, material: 'horn' },
|
|
237
|
+
{ bone: 'head', shape: 'roundCone', from: [0.13, 0.4, 0], to: [0.18, 0.56, -0.02], r1: 0.045, r2: 0.012, blend: 0.02, material: 'horn' },
|
|
238
|
+
],
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
One unicorn horn: `from: [0, 0.42, 0.06], to: [0, 0.64, 0.1], r1: 0.05, r2: 0.01`.
|
|
242
|
+
|
|
243
|
+
### Antenna with a glowing bulb
|
|
244
|
+
|
|
245
|
+
The bulb takes the colour of the current state (`glows: true`).
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
materials: {
|
|
249
|
+
stalk: { color: '#3a3f4c', f0: 0.04, spec: 0.8, shininess: 60, rim: 0.5 },
|
|
250
|
+
bulb: { color: '#ffffff', f0: 0.05, spec: 1, shininess: 120, rim: 0, glows: true, emitStrength: 1.4 },
|
|
251
|
+
},
|
|
252
|
+
bones: [
|
|
253
|
+
{ name: 'antenna', parent: 'head', pivot: [0, 0.45, 0], jiggle: { axis: [0, 1, 0], length: 0.16, freq: 2.6, damping: 0.1 } },
|
|
254
|
+
],
|
|
255
|
+
parts: [
|
|
256
|
+
{ bone: 'antenna', shape: 'capsule', from: [0, -0.02, 0], to: [0, 0.13, 0], radius: 0.012, material: 'stalk' },
|
|
257
|
+
{ bone: 'antenna', shape: 'sphere', at: [0, 0.16, 0], radius: 0.035, material: 'bulb' },
|
|
258
|
+
],
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Move the `top` anchor above the bulb: `top: { bone: 'antenna', at: [0, 0.22, 0] }`.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Things to wear
|
|
266
|
+
|
|
267
|
+
### Party hat with a pom-pom
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
materials: {
|
|
271
|
+
hat: { color: '#7c5cff', f0: 0.06, spec: 1.6, shininess: 160, rim: 0.5, gloss: 0.8 },
|
|
272
|
+
pom: { color: '#ffd84d', f0: 0.03, spec: 0.4, shininess: 20, rim: 1, sheen: 0.7 },
|
|
273
|
+
},
|
|
274
|
+
parts: [
|
|
275
|
+
{ bone: 'head', shape: 'roundCone', at: [0.05, 0.44, 0], rot: [0, 0, -0.25], from: [0, 0, 0], to: [0, 0.2, 0], r1: 0.1, r2: 0.012, material: 'hat' },
|
|
276
|
+
{ bone: 'head', shape: 'sphere', at: [0.1, 0.64, 0], radius: 0.035, material: 'pom' },
|
|
277
|
+
],
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Stripes on the hat: sphere paints along it (see "Stripes" below).
|
|
281
|
+
|
|
282
|
+
### Beanie
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
materials: {
|
|
286
|
+
knit: { color: '#e8433f', f0: 0.02, spec: 0.15, shininess: 10, rim: 0.7, sheen: 0.7, surface: { kind: 'scales', scale: 42, strength: 0.75, color: '#a8242a', bone: 'head' } },
|
|
287
|
+
},
|
|
288
|
+
parts: [
|
|
289
|
+
/* the top half of an ellipsoid a little larger than the head */
|
|
290
|
+
{ bone: 'head', shape: 'ellipsoid', at: [0, 0.27, -0.01], radii: [0.32, 0.23, 0.29], clip: { normal: [0, -1, 0] }, material: 'knit' },
|
|
291
|
+
{ bone: 'head', shape: 'cylinder', at: [0, 0.29, -0.01], radius: 0.315, half: 0.035, round: 0.03, material: 'knit' },
|
|
292
|
+
{ bone: 'head', shape: 'sphere', at: [0, 0.52, -0.01], radius: 0.06, material: 'knit' },
|
|
293
|
+
],
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Keep the eyes below the beanie's rim (eye y under 0.24).
|
|
297
|
+
|
|
298
|
+
### Headphones
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
materials: {
|
|
302
|
+
band: { color: '#20253a', f0: 0.06, spec: 1.4, shininess: 120, rim: 0.4, gloss: 0.7 },
|
|
303
|
+
cup: { color: '#4f7cff', f0: 0.08, spec: 1.6, shininess: 160, rim: 0.5, gloss: 0.8 },
|
|
304
|
+
},
|
|
305
|
+
parts: [
|
|
306
|
+
/* the band: a ring around the head's outline, facing the camera, cut to its top half */
|
|
307
|
+
{ bone: 'head', shape: 'frame', at: [0, 0.2, 0], half: [0.33, 0.29], corner: 0.29, width: 0.015, depth: 0.025, clip: { normal: [0, -1, 0] }, material: 'band' },
|
|
308
|
+
/* the cups: cylinders turned on their side (rz 90°) */
|
|
309
|
+
{ bone: 'head', shape: 'cylinder', at: [-0.33, 0.18, 0], rot: [0, 0, 1.5708], radius: 0.1, half: 0.045, round: 0.02, material: 'cup' },
|
|
310
|
+
{ bone: 'head', shape: 'cylinder', at: [0.33, 0.18, 0], rot: [0, 0, 1.5708], radius: 0.1, half: 0.045, round: 0.02, material: 'cup' },
|
|
311
|
+
],
|
|
312
|
+
anchors: {
|
|
313
|
+
ear: { bone: 'head', at: [-0.38, 0.18, 0] },
|
|
314
|
+
},
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The band's `half` is the head's x and y radii plus 0.02, so it hugs the head
|
|
318
|
+
without touching it. Move the `ear` anchor onto a cup, as above.
|
|
319
|
+
|
|
320
|
+
### Round glasses
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
materials: {
|
|
324
|
+
rims: { color: '#2b2b33', f0: 0.06, spec: 1.6, shininess: 160, rim: 0.4, gloss: 0.8 },
|
|
325
|
+
},
|
|
326
|
+
parts: [
|
|
327
|
+
/* a frame shape faces the camera; one ring per eye, in front of it */
|
|
328
|
+
{ bone: 'head', shape: 'frame', at: [-0.11, 0.2, 0.275], half: [0.075, 0.075], corner: 0.075, width: 0.01, depth: 0.008, material: 'rims' },
|
|
329
|
+
{ bone: 'head', shape: 'frame', at: [0.11, 0.2, 0.275], half: [0.075, 0.075], corner: 0.075, width: 0.01, depth: 0.008, material: 'rims' },
|
|
330
|
+
{ bone: 'head', shape: 'capsule', from: [-0.035, 0.21, 0.285], to: [0.035, 0.21, 0.285], radius: 0.009, material: 'rims' },
|
|
331
|
+
],
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Square glasses: a smaller `corner` (0.02). Put the rings just in front of the
|
|
335
|
+
eyes: their z is the face's surface plus about 0.005.
|
|
336
|
+
|
|
337
|
+
### Bow
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
materials: {
|
|
341
|
+
bow: { color: '#ff5d8f', f0: 0.06, spec: 1.6, shininess: 160, rim: 0.5, gloss: 0.8 },
|
|
342
|
+
},
|
|
343
|
+
parts: [
|
|
344
|
+
{ bone: 'head', shape: 'ellipsoid', at: [0.2, 0.43, 0.02], rot: [0, 0, 0.5], radii: [0.075, 0.05, 0.03], material: 'bow' },
|
|
345
|
+
{ bone: 'head', shape: 'ellipsoid', at: [0.31, 0.39, 0.02], rot: [0, 0, -0.3], radii: [0.075, 0.05, 0.03], material: 'bow' },
|
|
346
|
+
{ bone: 'head', shape: 'sphere', at: [0.26, 0.41, 0.03], radius: 0.035, material: 'bow' },
|
|
347
|
+
],
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Faces
|
|
353
|
+
|
|
354
|
+
### Snout with a glossy nose (dog, bear, pig)
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
materials: {
|
|
358
|
+
nose: { color: '#1d1410', f0: 0.06, spec: 1.8, shininess: 200, rim: 0.3, gloss: 0.9 },
|
|
359
|
+
snout: { color: '#ffe3c2', f0: 0.04, spec: 0.8, shininess: 60, rim: 0.8, sss: 0.2, sssColor: '#fff0dc' },
|
|
360
|
+
},
|
|
361
|
+
parts: [
|
|
362
|
+
{ bone: 'head', shape: 'ellipsoid', at: [0, 0.1, 0.22], radii: [0.12, 0.08, 0.09], blend: 0.04, material: 'snout' },
|
|
363
|
+
{ bone: 'head', shape: 'ellipsoid', at: [0, 0.14, 0.31], radii: [0.04, 0.03, 0.025], material: 'nose' },
|
|
364
|
+
],
|
|
365
|
+
face: { kind: 'mouth', bone: 'head', on: ['snout'], at: [0, 0.07, 0.3], width: 0.045, color: '#4a2418', tongue: '#ff7d8a' },
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
With a snout, the mouth goes on the snout (`on: ['snout']`), and the eyes
|
|
369
|
+
move up a little (y 0.24). Pig: a pink snout with two nostrils carved by
|
|
370
|
+
subtraction, listed right after it:
|
|
371
|
+
`{ bone: 'head', shape: 'sphere', at: [-0.03, 0.1, 0.31], radius: 0.014, op: 'sub', material: 'snout' }` and its mirror.
|
|
372
|
+
|
|
373
|
+
### Small beak (bird, duck, penguin)
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
materials: {
|
|
377
|
+
beak: { color: '#ff7a1a', f0: 0.05, spec: 1, shininess: 80, rim: 0.4, gloss: 0.6 },
|
|
378
|
+
},
|
|
379
|
+
parts: [
|
|
380
|
+
{ bone: 'head', shape: 'roundCone', from: [0, 0.12, 0.24], to: [0, 0.09, 0.37], r1: 0.065, r2: 0.015, material: 'beak' },
|
|
381
|
+
],
|
|
382
|
+
/* the mouth moves under the beak */
|
|
383
|
+
face: { kind: 'mouth', bone: 'head', on: ['skin'], at: [0, 0.03, 0.22], width: 0.04, color: '#5a2a1a', tongue: '#ff7d8a' },
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
A beak replaces the mouth: leave out `face`, or keep a tiny mouth below the
|
|
387
|
+
beak. For a beak that opens when speaking, see `pip` (two halves on `beak`
|
|
388
|
+
and `jaw` bones driven by `talk`).
|
|
389
|
+
|
|
390
|
+
### Freckles, whiskers and a belly
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
paints: [
|
|
394
|
+
/* freckles: tiny sphere paints on the surface under the eyes */
|
|
395
|
+
{ bone: 'head', on: ['skin'], kind: 'sphere', at: [-0.17, 0.12, 0.23], radius: 0.012, color: '#c46a3a', soft: 0.2 },
|
|
396
|
+
{ bone: 'head', on: ['skin'], kind: 'sphere', at: [-0.2, 0.14, 0.21], radius: 0.012, color: '#c46a3a', soft: 0.2 },
|
|
397
|
+
{ bone: 'head', on: ['skin'], kind: 'sphere', at: [0.17, 0.12, 0.23], radius: 0.012, color: '#c46a3a', soft: 0.2 },
|
|
398
|
+
{ bone: 'head', on: ['skin'], kind: 'sphere', at: [0.2, 0.14, 0.21], radius: 0.012, color: '#c46a3a', soft: 0.2 },
|
|
399
|
+
/* a pale belly on the front of the body */
|
|
400
|
+
{ bone: 'body', on: ['skin'], kind: 'ellipse', at: [0, 0.16, 0.12], radii: [0.12, 0.1], color: '#fff2dc', soft: 0.3 },
|
|
401
|
+
],
|
|
402
|
+
parts: [
|
|
403
|
+
/* whiskers: thin capsules sticking out of the cheeks */
|
|
404
|
+
{ bone: 'head', shape: 'capsule', from: [-0.2, 0.1, 0.2], to: [-0.36, 0.13, 0.2], radius: 0.006, material: 'whisker' },
|
|
405
|
+
{ bone: 'head', shape: 'capsule', from: [-0.2, 0.08, 0.2], to: [-0.36, 0.06, 0.2], radius: 0.006, material: 'whisker' },
|
|
406
|
+
{ bone: 'head', shape: 'capsule', from: [0.2, 0.1, 0.2], to: [0.36, 0.13, 0.2], radius: 0.006, material: 'whisker' },
|
|
407
|
+
{ bone: 'head', shape: 'capsule', from: [0.2, 0.08, 0.2], to: [0.36, 0.06, 0.2], radius: 0.006, material: 'whisker' },
|
|
408
|
+
],
|
|
409
|
+
materials: {
|
|
410
|
+
whisker: { color: '#fff8ee', f0: 0.04, spec: 0.6, shininess: 60, rim: 0.5 },
|
|
411
|
+
},
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## Patterns
|
|
417
|
+
|
|
418
|
+
### Stripes (bee, tiger, sweater)
|
|
419
|
+
|
|
420
|
+
Paint with huge, hard-edged spheres whose edges cross the body. Each sphere
|
|
421
|
+
paints everything inside it, so a sphere centred far below the body paints a
|
|
422
|
+
band at its top edge; later paints cover earlier ones.
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
paints: [
|
|
426
|
+
/* bands across the body: each sphere colours everything below its top edge */
|
|
427
|
+
{ bone: 'body', on: ['skin'], kind: 'sphere', at: [0, -0.55, 0], radius: 0.8, color: '#2b2118', soft: 0.02 },
|
|
428
|
+
{ bone: 'body', on: ['skin'], kind: 'sphere', at: [0, -0.63, 0], radius: 0.8, color: '#ffc21a', soft: 0.02 },
|
|
429
|
+
{ bone: 'body', on: ['skin'], kind: 'sphere', at: [0, -0.71, 0], radius: 0.8, color: '#2b2118', soft: 0.02 },
|
|
430
|
+
],
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Each band's top edge is at `at.y + radius` (0.25, 0.17 and 0.09 here, on a
|
|
434
|
+
body from y 0 to 0.38); each paint covers the lower part of the one before
|
|
435
|
+
it, leaving a band. Small `soft` keeps the edges crisp. For more bands, add
|
|
436
|
+
more spheres, each 0.08 lower.
|
|
437
|
+
|
|
438
|
+
### Spots (mushroom, ladybird, cow, dalmatian)
|
|
439
|
+
|
|
440
|
+
Sphere paints with `at` **on the surface** (use the formula at the top of
|
|
441
|
+
this file). Vary the radius (0.03 to 0.07) and spread them unevenly, with a
|
|
442
|
+
few on the back and sides so the character looks right when it turns.
|
|
443
|
+
|
|
444
|
+
```ts
|
|
445
|
+
paints: [
|
|
446
|
+
{ bone: 'head', on: ['skin'], kind: 'sphere', at: [0.143, 0.343, 0.2], radius: 0.05, color: '#ffffff', soft: 0.15 },
|
|
447
|
+
{ bone: 'head', on: ['skin'], kind: 'sphere', at: [-0.19, 0.38, 0.1], radius: 0.04, color: '#ffffff', soft: 0.15 },
|
|
448
|
+
],
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Raised spots (bumps you can see from the side) are small `sphere` parts half
|
|
452
|
+
buried in the surface instead of paints; see `berry` and `cap`.
|
|
453
|
+
|
|
454
|
+
### Patches (panda eye patches, a dark muzzle)
|
|
455
|
+
|
|
456
|
+
Use parts with their own material rather than paints when the patch should
|
|
457
|
+
have a raised edge, and paints when it should be flat. Panda: see `bao`.
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Bodies that are objects
|
|
462
|
+
|
|
463
|
+
The face goes on the front of the object; the rest of the spec is the same.
|
|
464
|
+
|
|
465
|
+
### Cup or mug
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
materials: {
|
|
469
|
+
cup: { color: '#f4efe6', f0: 0.06, spec: 1.6, shininess: 140, rim: 0.6, gloss: 0.85 },
|
|
470
|
+
coffee: { color: '#6b3f22', f0: 0.04, spec: 1.2, shininess: 90, rim: 0.3, gloss: 0.7 },
|
|
471
|
+
},
|
|
472
|
+
bones: [
|
|
473
|
+
{ name: 'body', pivot: [0, 0, 0], drive: { ry: [['yaw', 0.4]], rz: [['roll', 0.4]], rx: [['pitch', -0.3]] } },
|
|
474
|
+
],
|
|
475
|
+
parts: [
|
|
476
|
+
{ bone: 'body', shape: 'cylinder', at: [0, 0.3, 0], radius: 0.28, half: 0.28, round: 0.06, material: 'cup' },
|
|
477
|
+
/* the opening: a slightly smaller cylinder carved out of the top */
|
|
478
|
+
{ bone: 'body', shape: 'cylinder', at: [0, 0.6, 0], radius: 0.24, half: 0.06, round: 0.02, op: 'sub', blend: 0.01, material: 'cup' },
|
|
479
|
+
/* the drink inside */
|
|
480
|
+
{ bone: 'body', shape: 'cylinder', at: [0, 0.53, 0], radius: 0.245, half: 0.012, round: 0.01, material: 'coffee' },
|
|
481
|
+
/* the handle: a capsule loop on the side */
|
|
482
|
+
{ bone: 'body', shape: 'capsule', from: [0.27, 0.45, 0], to: [0.38, 0.4, 0], radius: 0.035, material: 'cup' },
|
|
483
|
+
{ bone: 'body', shape: 'capsule', from: [0.38, 0.4, 0], to: [0.38, 0.2, 0], radius: 0.035, material: 'cup' },
|
|
484
|
+
{ bone: 'body', shape: 'capsule', from: [0.38, 0.2, 0], to: [0.27, 0.16, 0], radius: 0.035, material: 'cup' },
|
|
485
|
+
],
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
On a one-bone character, every `bone` (eyes, face, paints, anchors) is
|
|
489
|
+
`'body'`. The full version with foam art and steam is `latte`.
|
|
490
|
+
|
|
491
|
+
### Toaster (a box with a slot and something popping out)
|
|
492
|
+
|
|
493
|
+
A complete object character: one `body` bone carries the box, the face and
|
|
494
|
+
the anchors; the toast has its own springy bone so it bobs.
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
materials: {
|
|
498
|
+
shell: { color: '#e5483f', f0: 0.06, spec: 1.6, shininess: 160, rim: 0.5, gloss: 0.8 },
|
|
499
|
+
toast: { color: '#d9913f', f0: 0.03, spec: 0.5, shininess: 30, rim: 0.5, sheen: 0.3 },
|
|
500
|
+
crumb: { color: '#f3d9a4', f0: 0.03, spec: 0.3, shininess: 20, rim: 0.4 },
|
|
501
|
+
chrome: { color: '#8f98ae', f0: 0.45, spec: 1, shininess: 120, metal: 0.85, rim: 0.3, gloss: 0.7 },
|
|
502
|
+
foot: { color: '#26252b', f0: 0.04, spec: 0.6, shininess: 40, rim: 0.3 },
|
|
503
|
+
},
|
|
504
|
+
bones: [
|
|
505
|
+
{ name: 'body', pivot: [0, 0, 0], drive: { ry: [['yaw', 0.4]], rz: [['roll', 0.3]], rx: [['pitch', -0.25]] } },
|
|
506
|
+
{ name: 'toast', parent: 'body', pivot: [0, 0.5, 0], jiggle: { axis: [0, 1, 0], length: 0.14, freq: 3, damping: 0.15, maxAngle: 0.35 } },
|
|
507
|
+
],
|
|
508
|
+
parts: [
|
|
509
|
+
{ bone: 'body', shape: 'roundBox', at: [0, 0.29, 0], half: [0.3, 0.22, 0.2], round: 0.07, material: 'shell' },
|
|
510
|
+
/* the slot: listed right after the box so it carves it */
|
|
511
|
+
{ bone: 'body', shape: 'roundBox', at: [0, 0.5, 0], half: [0.19, 0.05, 0.035], round: 0.02, op: 'sub', material: 'shell' },
|
|
512
|
+
{ bone: 'body', shape: 'capsule', from: [0.31, 0.38, 0], to: [0.36, 0.38, 0], radius: 0.025, material: 'chrome' },
|
|
513
|
+
{ bone: 'body', shape: 'ellipsoid', at: [-0.18, 0.04, 0], radii: [0.06, 0.04, 0.12], material: 'foot' },
|
|
514
|
+
{ bone: 'body', shape: 'ellipsoid', at: [0.18, 0.04, 0], radii: [0.06, 0.04, 0.12], material: 'foot' },
|
|
515
|
+
/* the slice: a crust, with the soft crumb just in front of it */
|
|
516
|
+
{ bone: 'toast', shape: 'roundBox', at: [0, 0.08, 0], half: [0.16, 0.13, 0.022], round: 0.05, material: 'toast' },
|
|
517
|
+
{ bone: 'toast', shape: 'roundBox', at: [0, 0.08, 0.004], half: [0.135, 0.105, 0.022], round: 0.04, material: 'crumb' },
|
|
518
|
+
],
|
|
519
|
+
paints: [
|
|
520
|
+
{ bone: 'body', on: ['shell'], kind: 'sphere', at: [-0.19, 0.25, 0.2], radius: 0.045, color: '#ff9a9a', soft: 0.6, opacity: 0.8 },
|
|
521
|
+
{ bone: 'body', on: ['shell'], kind: 'sphere', at: [0.19, 0.25, 0.2], radius: 0.045, color: '#ff9a9a', soft: 0.6, opacity: 0.8 },
|
|
522
|
+
],
|
|
523
|
+
eyes: { kind: 'ball', bone: 'body', at: [[-0.1, 0.32, 0.165], [0.1, 0.32, 0.165]], radius: 0.05, iris: '#2b1d14', irisSize: 0.8, pupilSize: 0.5, lid: 'shell' },
|
|
524
|
+
face: { kind: 'mouth', bone: 'body', on: ['shell'], at: [0, 0.23, 0.2], width: 0.05, color: '#5a1a14', tongue: '#ff7d8a' },
|
|
525
|
+
anchors: {
|
|
526
|
+
top: { bone: 'toast', at: [0.12, 0.24, 0] },
|
|
527
|
+
mouth: { bone: 'body', at: [0, 0.23, 0.23] },
|
|
528
|
+
ear: { bone: 'body', at: [-0.31, 0.32, 0] },
|
|
529
|
+
feet: { bone: 'root', at: [0, 0.02, 0.15] },
|
|
530
|
+
},
|
|
531
|
+
acting: {
|
|
532
|
+
celebrating: { toast: { rz: [0, 0.25, 4, 0] } },
|
|
533
|
+
sleeping: { toast: { rx: 0.35 } },
|
|
534
|
+
sad: { toast: { rx: 0.45, rz: 0.2 } },
|
|
535
|
+
error: { toast: { rz: [0, 0.2, 11, 0] } },
|
|
536
|
+
},
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Flower pot
|
|
540
|
+
|
|
541
|
+
A `roundCone` wider at the top, cut flat at the top with a clip: see `cacti`.
|
|
542
|
+
|
|
543
|
+
### Box robot
|
|
544
|
+
|
|
545
|
+
A `roundBox` head with `round` about 0.06, a `plate` screen on the front and
|
|
546
|
+
an LED `screen` face instead of eyes and a mouth: see `volt`.
|
|
547
|
+
|
|
548
|
+
### Floating blob (cloud, ghost, slime)
|
|
549
|
+
|
|
550
|
+
Several spheres melted together with `blend: 0.1` to `0.15`, and
|
|
551
|
+
`motion: { float: 0.15 }`: see `drift` and `boo`.
|