nx_movelib.nx source
↩ module page · 228 lines · 13683 B
1// nx_movelib.nx -- MOTION AS DATA (swap+motion census SCOPE B, the operator's headline: a synthetic human wants
2// a move that isn't in the library -> BUILD it). A MOVE is DATA: keyframes over a loop period, each keyframe = a
3// per-bone {yaw,pitch} pose. move_gen is a PARAMETRIC generator (data-driven vocabulary, grows by adding a
4// recipe -- exactly like nx_t2mesh grows objects; NOT neural MDM, which is the S6 rung). move_pose_at interpolates
5// the keyframes and poses the skeleton via nx_skeleton FK. Both the synthetic human AND the game system draw from
6// the same library. Angles in rad*4096 (nx_itrig). license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_skeleton.nx"
9import "nx_itrig.nx"
10import "nx_figure_render.nx" // RIG_NB + RG_* bone-index consts (the rig moves target)
11const MOVE_MAGIC_12868: i64 = 12868
12const MOVE_MAGIC_1200: i64 = 1200
13const MOVE_MAGIC_1600: i64 = 1600
14const MOVE_MAGIC_1400: i64 = 1400
15const MOVE_MAGIC_1800: i64 = 1800
16const MOVE_MAGIC_1500: i64 = 1500
17const MOVE_MAGIC_2000: i64 = 2000
18const MOVE_MAGIC_4096: i64 = 4096
19
20const MOVE_MAXK: i64 = 8 // max keyframes
21
22func mv_hdr(base: i64) -> *i64 { return base as *i64 } // [0]=nk [1]=period_ms
23func mv_time(base: i64) -> *i64 { return (base + 64) as *i64 } // [k] = key time ms
24// pose slot for (key k, bone b): yaw at +0, pitch at +8 (2 i64)
25func mv_pose(base: i64, k: i64, b: i64) -> *i64 { return (base + 64 + MOVE_MAXK * 8 + (k * RIG_NB + b) * 16) as *i64 }
26func move_bytes() -> i64 { return 64 + MOVE_MAXK * 8 + MOVE_MAXK * RIG_NB * 16 + 64 }
27
28// deg -> rad*4096 (IT_PI 12868 == 180 deg)
29func md_deg(d: i64) -> i64 { return d * MOVE_MAGIC_12868 / 180 }
30
31func mv_zero(base: i64) -> i64 {
32 var k: i64 = 0
33 while k < MOVE_MAXK {
34 var b: i64 = 0
35 while b < RIG_NB { let p: *i64 = mv_pose(base, k, b); p[0] = 0; p[1] = 0; b = b + 1 }
36 k = k + 1
37 }
38 return 0
39}
40func mv_set(base: i64, k: i64, b: i64, yaw: i64, pitch: i64) -> i64 { let p: *i64 = mv_pose(base, k, b); p[0] = yaw; p[1] = pitch; return 0 }
41// author evenly-spaced key TIMES over [0, period] (nk keys; key nk-1 sits at period == a copy of key 0 for a seamless loop)
42func mv_times(base: i64, nk: i64, period: i64) -> i64 {
43 let h: *i64 = mv_hdr(base); h[0] = nk; h[1] = period
44 let t: *i64 = mv_time(base)
45 var k: i64 = 0
46 while k < nk { t[k] = k * period / (nk - 1); k = k + 1 }
47 return 0
48}
49
50// ---- the move vocabulary (parametric; grows as data) --------------------------------------------------
51func move_name(id: i64) -> *u8 {
52 if id == 1 { return "wave" as *u8 }
53 if id == 2 { return "cheer" as *u8 }
54 if id == 3 { return "sway" as *u8 }
55 if id == 4 { return "kick" as *u8 }
56 if id == 5 { return "lookaround" as *u8 }
57 if id == 6 { return "armcircle" as *u8 }
58 if id == 7 { return "clap" as *u8 }
59 if id == 8 { return "punch" as *u8 }
60 if id == 9 { return "bow" as *u8 }
61 if id == 10 { return "jumpingjack" as *u8 }
62 return "idle" as *u8
63}
64func move_id_of(prompt: *u8) -> i64 {
65 if mv_has(prompt, "wave" as *u8) == 1 { return 1 }
66 if mv_has(prompt, "hello" as *u8) == 1 { return 1 }
67 if mv_has(prompt, "cheer" as *u8) == 1 { return 2 }
68 if mv_has(prompt, "celebrat" as *u8) == 1 { return 2 }
69 if mv_has(prompt, "victory" as *u8) == 1 { return 2 }
70 if mv_has(prompt, "sway" as *u8) == 1 { return 3 }
71 if mv_has(prompt, "dance" as *u8) == 1 { return 3 }
72 if mv_has(prompt, "kick" as *u8) == 1 { return 4 }
73 if mv_has(prompt, "march" as *u8) == 1 { return 4 }
74 if mv_has(prompt, "look" as *u8) == 1 { return 5 }
75 if mv_has(prompt, "circle" as *u8) == 1 { return 6 }
76 if mv_has(prompt, "stretch" as *u8) == 1 { return 6 }
77 if mv_has(prompt, "clap" as *u8) == 1 { return 7 }
78 if mv_has(prompt, "applau" as *u8) == 1 { return 7 }
79 if mv_has(prompt, "punch" as *u8) == 1 { return 8 }
80 if mv_has(prompt, "jab" as *u8) == 1 { return 8 }
81 if mv_has(prompt, "bow" as *u8) == 1 { return 9 }
82 if mv_has(prompt, "jump" as *u8) == 1 { return 10 }
83 if mv_has(prompt, "jack" as *u8) == 1 { return 10 }
84 return 0
85}
86func mv_has(s: *u8, kw: *u8) -> i64 {
87 var i: i64 = 0
88 while s[i] != (0 as u8) {
89 var k: i64 = 0
90 var stop: i64 = 0
91 while stop == 0 {
92 if kw[k] == (0 as u8) { stop = 1 } else {
93 let sc: *u8 = (s as i64 + i + k) as *u8
94 if sc[0] != kw[k] { stop = 2 } else { k = k + 1 }
95 }
96 }
97 if stop == 1 { return 1 }
98 i = i + 1
99 }
100 return 0
101}
102
103// build a move recipe into base for the given id (the parametric "generator"). Pitch(Rz) raises a limb in the
104// frontal plane; yaw(Ry) turns it. Right-side raises are +pitch, left-side raises are -pitch (mirror).
105func move_gen(base: i64, id: i64) -> i64 {
106 if id == 1 { // WAVE: right arm raised, swinging up/down (visible wave)
107 mv_times(base, 5, MOVE_MAGIC_1200); mv_zero(base)
108 mv_set(base, 0, RG_SHR, 0, md_deg(48)); mv_set(base, 0, RG_ELR, md_deg(16), 0)
109 mv_set(base, 1, RG_SHR, 0, md_deg(76)); mv_set(base, 1, RG_ELR, 0 - md_deg(14), 0)
110 mv_set(base, 2, RG_SHR, 0, md_deg(48)); mv_set(base, 2, RG_ELR, md_deg(16), 0)
111 mv_set(base, 3, RG_SHR, 0, md_deg(76)); mv_set(base, 3, RG_ELR, 0 - md_deg(14), 0)
112 mv_set(base, 4, RG_SHR, 0, md_deg(48)); mv_set(base, 4, RG_ELR, md_deg(16), 0)
113 return 0
114 }
115 if id == 2 { // CHEER: both arms up, small bounce
116 mv_times(base, 4, 1000); mv_zero(base)
117 mv_set(base, 0, RG_SHR, 0, md_deg(60)); mv_set(base, 0, RG_SHL, 0, 0 - md_deg(60))
118 mv_set(base, 1, RG_SHR, 0, md_deg(72)); mv_set(base, 1, RG_SHL, 0, 0 - md_deg(72))
119 mv_set(base, 2, RG_SHR, 0, md_deg(60)); mv_set(base, 2, RG_SHL, 0, 0 - md_deg(60))
120 mv_set(base, 3, RG_SHR, 0, md_deg(60)); mv_set(base, 3, RG_SHL, 0, 0 - md_deg(60))
121 return 0
122 }
123 if id == 3 { // SWAY / dance: spine + head sway, arms counter-swing
124 mv_times(base, 5, MOVE_MAGIC_1600); mv_zero(base)
125 mv_set(base, 0, RG_SPINE, md_deg(16), 0); mv_set(base, 0, RG_NECK, 0 - md_deg(12), 0); mv_set(base, 0, RG_SHR, 0, md_deg(28)); mv_set(base, 0, RG_SHL, 0, 0 - md_deg(14))
126 mv_set(base, 1, RG_SPINE, 0 - md_deg(16), 0); mv_set(base, 1, RG_NECK, md_deg(12), 0); mv_set(base, 1, RG_SHR, 0, md_deg(14)); mv_set(base, 1, RG_SHL, 0, 0 - md_deg(28))
127 mv_set(base, 2, RG_SPINE, md_deg(16), 0); mv_set(base, 2, RG_NECK, 0 - md_deg(12), 0); mv_set(base, 2, RG_SHR, 0, md_deg(28)); mv_set(base, 2, RG_SHL, 0, 0 - md_deg(14))
128 mv_set(base, 3, RG_SPINE, 0 - md_deg(16), 0); mv_set(base, 3, RG_NECK, md_deg(12), 0); mv_set(base, 3, RG_SHR, 0, md_deg(14)); mv_set(base, 3, RG_SHL, 0, 0 - md_deg(28))
129 mv_set(base, 4, RG_SPINE, md_deg(16), 0); mv_set(base, 4, RG_NECK, 0 - md_deg(12), 0); mv_set(base, 4, RG_SHR, 0, md_deg(28)); mv_set(base, 4, RG_SHL, 0, 0 - md_deg(14))
130 return 0
131 }
132 if id == 4 { // KICK / march: legs alternate, arms counter
133 mv_times(base, 5, MOVE_MAGIC_1400); mv_zero(base)
134 mv_set(base, 0, RG_HIPR, 0, md_deg(40)); mv_set(base, 0, RG_SHL, 0, 0 - md_deg(30))
135 mv_set(base, 1, RG_HIPL, 0, 0 - md_deg(40)); mv_set(base, 1, RG_SHR, 0, md_deg(30))
136 mv_set(base, 2, RG_HIPR, 0, md_deg(40)); mv_set(base, 2, RG_SHL, 0, 0 - md_deg(30))
137 mv_set(base, 3, RG_HIPL, 0, 0 - md_deg(40)); mv_set(base, 3, RG_SHR, 0, md_deg(30))
138 mv_set(base, 4, RG_HIPR, 0, md_deg(40)); mv_set(base, 4, RG_SHL, 0, 0 - md_deg(30))
139 return 0
140 }
141 if id == 5 { // LOOKAROUND: head + upper spine yaw sweep
142 mv_times(base, 5, MOVE_MAGIC_1800); mv_zero(base)
143 mv_set(base, 0, RG_NECK, md_deg(35), 0); mv_set(base, 0, RG_CHEST, md_deg(10), 0)
144 mv_set(base, 1, RG_NECK, 0, 0)
145 mv_set(base, 2, RG_NECK, 0 - md_deg(35), 0); mv_set(base, 2, RG_CHEST, 0 - md_deg(10), 0)
146 mv_set(base, 3, RG_NECK, 0, 0)
147 mv_set(base, 4, RG_NECK, md_deg(35), 0); mv_set(base, 4, RG_CHEST, md_deg(10), 0)
148 return 0
149 }
150 if id == 6 { // ARMCIRCLE / stretch: both arms sweep through pitch phases
151 mv_times(base, 5, MOVE_MAGIC_1500); mv_zero(base)
152 mv_set(base, 0, RG_SHR, 0, md_deg(18)); mv_set(base, 0, RG_SHL, 0, 0 - md_deg(18))
153 mv_set(base, 1, RG_SHR, 0, md_deg(50)); mv_set(base, 1, RG_SHL, 0, 0 - md_deg(50))
154 mv_set(base, 2, RG_SHR, 0, md_deg(78)); mv_set(base, 2, RG_SHL, 0, 0 - md_deg(78))
155 mv_set(base, 3, RG_SHR, 0, md_deg(50)); mv_set(base, 3, RG_SHL, 0, 0 - md_deg(50))
156 mv_set(base, 4, RG_SHR, 0, md_deg(18)); mv_set(base, 4, RG_SHL, 0, 0 - md_deg(18))
157 return 0
158 }
159 if id == 7 { // CLAP: forearms swing IN together, then OUT apart (rhythmic)
160 mv_times(base, 5, 900); mv_zero(base)
161 mv_set(base, 0, RG_SHR, 0, md_deg(38)); mv_set(base, 0, RG_ELR, 0 - md_deg(28), 0); mv_set(base, 0, RG_SHL, 0, 0 - md_deg(38)); mv_set(base, 0, RG_ELL, md_deg(28), 0)
162 mv_set(base, 1, RG_SHR, 0, md_deg(42)); mv_set(base, 1, RG_ELR, md_deg(40), 0); mv_set(base, 1, RG_SHL, 0, 0 - md_deg(42)); mv_set(base, 1, RG_ELL, 0 - md_deg(40), 0)
163 mv_set(base, 2, RG_SHR, 0, md_deg(38)); mv_set(base, 2, RG_ELR, 0 - md_deg(28), 0); mv_set(base, 2, RG_SHL, 0, 0 - md_deg(38)); mv_set(base, 2, RG_ELL, md_deg(28), 0)
164 mv_set(base, 3, RG_SHR, 0, md_deg(42)); mv_set(base, 3, RG_ELR, md_deg(40), 0); mv_set(base, 3, RG_SHL, 0, 0 - md_deg(42)); mv_set(base, 3, RG_ELL, 0 - md_deg(40), 0)
165 mv_set(base, 4, RG_SHR, 0, md_deg(38)); mv_set(base, 4, RG_ELR, 0 - md_deg(28), 0); mv_set(base, 4, RG_SHL, 0, 0 - md_deg(38)); mv_set(base, 4, RG_ELL, md_deg(28), 0)
166 return 0
167 }
168 if id == 8 { // PUNCH: alternating forward jabs (one arm extends, other guards)
169 mv_times(base, 5, 1000); mv_zero(base)
170 mv_set(base, 0, RG_SHR, 0, md_deg(16)); mv_set(base, 0, RG_ELR, 0, 0); mv_set(base, 0, RG_SHL, 0, 0 - md_deg(20)); mv_set(base, 0, RG_ELL, md_deg(72), 0)
171 mv_set(base, 1, RG_SHR, 0, md_deg(16)); mv_set(base, 1, RG_ELR, md_deg(72), 0); mv_set(base, 1, RG_SHL, 0, 0 - md_deg(16)); mv_set(base, 1, RG_ELL, 0, 0)
172 mv_set(base, 2, RG_SHR, 0, md_deg(16)); mv_set(base, 2, RG_ELR, 0, 0); mv_set(base, 2, RG_SHL, 0, 0 - md_deg(20)); mv_set(base, 2, RG_ELL, md_deg(72), 0)
173 mv_set(base, 3, RG_SHR, 0, md_deg(16)); mv_set(base, 3, RG_ELR, md_deg(72), 0); mv_set(base, 3, RG_SHL, 0, 0 - md_deg(16)); mv_set(base, 3, RG_ELL, 0, 0)
174 mv_set(base, 4, RG_SHR, 0, md_deg(16)); mv_set(base, 4, RG_ELR, 0, 0); mv_set(base, 4, RG_SHL, 0, 0 - md_deg(20)); mv_set(base, 4, RG_ELL, md_deg(72), 0)
175 return 0
176 }
177 if id == 9 { // BOW: bend forward at the spine, head down, return
178 mv_times(base, 4, MOVE_MAGIC_1600); mv_zero(base)
179 mv_set(base, 1, RG_SPINE, 0, md_deg(44)); mv_set(base, 1, RG_NECK, 0, md_deg(22)); mv_set(base, 1, RG_SHR, 0, md_deg(14)); mv_set(base, 1, RG_SHL, 0, 0 - md_deg(14))
180 mv_set(base, 2, RG_SPINE, 0, md_deg(44)); mv_set(base, 2, RG_NECK, 0, md_deg(22)); mv_set(base, 2, RG_SHR, 0, md_deg(14)); mv_set(base, 2, RG_SHL, 0, 0 - md_deg(14))
181 return 0
182 }
183 if id == 10 { // JUMPING JACK: arms up + legs out, then down + together
184 mv_times(base, 5, 1000); mv_zero(base)
185 mv_set(base, 0, RG_SHR, 0, md_deg(8)); mv_set(base, 0, RG_SHL, 0, 0 - md_deg(8))
186 mv_set(base, 1, RG_SHR, 0, md_deg(86)); mv_set(base, 1, RG_SHL, 0, 0 - md_deg(86)); mv_set(base, 1, RG_HIPR, md_deg(22), 0); mv_set(base, 1, RG_HIPL, 0 - md_deg(22), 0)
187 mv_set(base, 2, RG_SHR, 0, md_deg(8)); mv_set(base, 2, RG_SHL, 0, 0 - md_deg(8))
188 mv_set(base, 3, RG_SHR, 0, md_deg(86)); mv_set(base, 3, RG_SHL, 0, 0 - md_deg(86)); mv_set(base, 3, RG_HIPR, md_deg(22), 0); mv_set(base, 3, RG_HIPL, 0 - md_deg(22), 0)
189 mv_set(base, 4, RG_SHR, 0, md_deg(8)); mv_set(base, 4, RG_SHL, 0, 0 - md_deg(8))
190 return 0
191 }
192 // idle: gentle spine sway
193 mv_times(base, 3, MOVE_MAGIC_2000); mv_zero(base)
194 mv_set(base, 0, RG_SPINE, md_deg(6), 0)
195 mv_set(base, 1, RG_SPINE, 0 - md_deg(6), 0)
196 mv_set(base, 2, RG_SPINE, md_deg(6), 0)
197 return 0
198}
199
200// interpolate the move at time t (ms), pose EVERY rig bone on the skeleton (rest bones stay rest), then the
201// caller runs sk_update. Loops over the move period.
202func move_pose_at(moveBase: i64, sk: i64, t: i64) -> i64 {
203 let h: *i64 = mv_hdr(moveBase)
204 let nk: i64 = h[0]
205 let period: i64 = h[1]
206 let tm: *i64 = mv_time(moveBase)
207 var tt: i64 = t
208 if period > 0 { tt = t % period }
209 if tt < 0 { tt = 0 }
210 // find segment [k, k+1] with tm[k] <= tt < tm[k+1]
211 var k: i64 = 0
212 var seg: i64 = 0
213 while k < nk - 1 { if tt >= tm[k] { if tt < tm[k + 1] { seg = k } } k = k + 1 }
214 let t0: i64 = tm[seg]
215 let t1: i64 = tm[seg + 1]
216 var frac: i64 = 0
217 if t1 > t0 { frac = (tt - t0) * MOVE_MAGIC_4096 / (t1 - t0) }
218 var b: i64 = 0
219 while b < RIG_NB {
220 let p0: *i64 = mv_pose(moveBase, seg, b)
221 let p1: *i64 = mv_pose(moveBase, seg + 1, b)
222 let yaw: i64 = p0[0] + (p1[0] - p0[0]) * frac / MOVE_MAGIC_4096
223 let pitch: i64 = p0[1] + (p1[1] - p0[1]) * frac / MOVE_MAGIC_4096
224 sk_pose(sk, b, yaw, pitch)
225 b = b + 1
226 }
227 return 0
228}