nx_quadruped.nx source
↩ module page · 183 lines · 10014 B
1// nx_quadruped.nx -- QUADRUPED rig + walk gait + body-fill: the bestiary's ANIMALS move too (operator thesis:
2// reuse synthetic assets "from animals to people to monsters" + give them motion). An 18-joint quadruped rig on
3// the SAME nx_skeleton FK substrate (root barrel -> chest/head + hips/tail + 4 three-bone legs), a DIAGONAL
4// WALK gait (FL+HR swing in phase, FR+HL antiphase -- the real quadruped walk coordination; legs point -y so a
5// pitch(Rz) swing moves the foot along the body axis x), and quadpose_fill = the wolf body as bone-anchored
6// ellipsoids + sphere-chain legs/tail on the CURRENT pose, rendering on the production shader like everything
7// else. Gait parameters are data (amplitudes/period per call). license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_bodypose.nx" // bp_part/bp_chain/bp_ball + skeleton/sdfrender/figure_render transitively
10import "nx_itrig.nx"
11const QR_MAGIC_12868: i64 = 12868
12const QR_MAGIC_25736: i64 = 25736
13const QR_MAGIC_4096: i64 = 4096
14const QR_MAGIC_6434: i64 = 6434
15const QR_MAGIC_2048: i64 = 2048
16const QR_MAGIC_2300: i64 = 2300
17const QR_MAGIC_2900: i64 = 2900
18
19const QR_ROOT: i64 = 0
20const QR_CHEST: i64 = 1
21const QR_HEAD: i64 = 2
22const QR_HIPS: i64 = 3
23const QR_FLHIP: i64 = 4
24const QR_FLKNEE: i64 = 5
25const QR_FLFOOT: i64 = 6
26const QR_FRHIP: i64 = 7
27const QR_FRKNEE: i64 = 8
28const QR_FRFOOT: i64 = 9
29const QR_HLHIP: i64 = 10
30const QR_HLKNEE: i64 = 11
31const QR_HLFOOT: i64 = 12
32const QR_HRHIP: i64 = 13
33const QR_HRKNEE: i64 = 14
34const QR_HRFOOT: i64 = 15
35const QR_TAIL1: i64 = 16
36const QR_TAIL2: i64 = 17
37
38func qdeg(d: i64) -> i64 { return d * QR_MAGIC_12868 / 180 }
39
40// build the rest rig (parent-first). Head end is -x (matches av_base_wolf's layout).
41func quad_build(sk: i64) -> i64 {
42 sk_init(sk)
43 sk_add_bone(sk, 0 - 1, 0, 0 - 100, 0) // 0 root (barrel center)
44 sk_add_bone(sk, QR_ROOT, 0 - 560, 120, 0) // 1 chest/shoulders
45 sk_add_bone(sk, QR_CHEST, 0 - 380, 300, 0) // 2 head
46 sk_add_bone(sk, QR_ROOT, 560, 60, 0) // 3 hips/haunches
47 sk_add_bone(sk, QR_CHEST, 0, 0 - 240, 90) // 4 FL hip
48 sk_add_bone(sk, QR_FLHIP, 0, 0 - 320, 0) // 5 FL knee
49 sk_add_bone(sk, QR_FLKNEE, 0, 0 - 320, 0) // 6 FL foot
50 sk_add_bone(sk, QR_CHEST, 0, 0 - 240, 0 - 90) // 7 FR hip
51 sk_add_bone(sk, QR_FRHIP, 0, 0 - 320, 0) // 8 FR knee
52 sk_add_bone(sk, QR_FRKNEE, 0, 0 - 320, 0) // 9 FR foot
53 sk_add_bone(sk, QR_HIPS, 0, 0 - 180, 90) // 10 HL hip
54 sk_add_bone(sk, QR_HLHIP, 0, 0 - 320, 0) // 11 HL knee
55 sk_add_bone(sk, QR_HLKNEE, 0, 0 - 320, 0) // 12 HL foot
56 sk_add_bone(sk, QR_HIPS, 0, 0 - 180, 0 - 90) // 13 HR hip
57 sk_add_bone(sk, QR_HRHIP, 0, 0 - 320, 0) // 14 HR knee
58 sk_add_bone(sk, QR_HRKNEE, 0, 0 - 320, 0) // 15 HR foot
59 sk_add_bone(sk, QR_HIPS, 340, 240, 0) // 16 tail base
60 sk_add_bone(sk, QR_TAIL1, 160, 160, 0) // 17 tail tip
61 return 0
62}
63
64// pose the WALK at phase t4096 in [0,4096) (one full stride). Diagonal pairs: FL+HR in phase, FR+HL antiphase.
65// swing_deg = leg swing amplitude (deg); call sk_update after.
66func quad_gait_pose(sk: i64, t4096: i64, swing_deg: i64) -> i64 {
67 let a: i64 = t4096 * QR_MAGIC_25736 / QR_MAGIC_4096 // phase angle (2*IT_PI = QR_MAGIC_25736)
68 let s: i64 = it_sin4096(a) // -QR_MAGIC_4096..QR_MAGIC_4096
69 let amp: i64 = qdeg(swing_deg)
70 let swing: i64 = amp * s / QR_MAGIC_4096
71 var kmag: i64 = swing
72 if kmag < 0 { kmag = 0 - kmag }
73 let kb: i64 = qdeg(9) + kmag / 2 // knees bend more mid-swing
74 let s2: i64 = it_sin4096(a * 2)
75 let bob: i64 = qdeg(3) * s2 / QR_MAGIC_4096 // body/head double-frequency bob
76 let tsw: i64 = qdeg(14) * it_sin4096(a + QR_MAGIC_6434) / QR_MAGIC_4096 // tail sway, quarter offset
77 sk_pose(sk, QR_ROOT, 0, bob)
78 sk_pose(sk, QR_HEAD, 0, 0 - bob)
79 sk_pose(sk, QR_FLHIP, 0, swing)
80 sk_pose(sk, QR_FLKNEE, 0, kb)
81 sk_pose(sk, QR_HRHIP, 0, swing)
82 sk_pose(sk, QR_HRKNEE, 0, kb)
83 sk_pose(sk, QR_FRHIP, 0, 0 - swing)
84 sk_pose(sk, QR_FRKNEE, 0, kb)
85 sk_pose(sk, QR_HLHIP, 0, 0 - swing)
86 sk_pose(sk, QR_HLKNEE, 0, kb)
87 sk_pose(sk, QR_TAIL1, tsw, qdeg(10))
88 sk_pose(sk, QR_TAIL2, tsw, qdeg(8))
89 return 0
90}
91
92// one leg's hip swing at phase a with a per-leg cycle offset (off in [0,4096) = fraction of the stride).
93func qleg(a: i64, ampfx: i64, off: i64) -> i64 { let p: i64 = a + off * QR_MAGIC_25736 / QR_MAGIC_4096; return ampfx * it_sin4096(p) / QR_MAGIC_4096 }
94func qabs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
95// GAIT-AS-DATA: pose at phase t4096 for gait 0=walk (4-beat diagonal), 1=trot (2-beat diagonal, suspension
96// bounce), 2=gallop (rotary, asymmetric leg phases + big spine gather/extend). Amplitudes + per-leg phase
97// offsets are the gait's data. call sk_update after.
98func quad_gait_pose_g(sk: i64, t4096: i64, gait: i64) -> i64 {
99 let a: i64 = t4096 * QR_MAGIC_25736 / QR_MAGIC_4096
100 var swing: i64 = qdeg(24)
101 var offFR: i64 = QR_MAGIC_2048; var offHL: i64 = QR_MAGIC_2048; var offHR: i64 = 0 // walk/trot: diagonal (FL+HR vs FR+HL)
102 var bobA: i64 = qdeg(3); var pitchA: i64 = 0; var kbase: i64 = qdeg(9)
103 if gait == 1 { swing = qdeg(31); bobA = qdeg(7); pitchA = qdeg(3); kbase = qdeg(14) } // trot: bouncier, more lift
104 if gait == 2 { swing = qdeg(36); offFR = 600; offHL = QR_MAGIC_2300; offHR = QR_MAGIC_2900; bobA = qdeg(4); pitchA = qdeg(11); kbase = qdeg(16) } // gallop: rotary + spine flex
105 let sFL: i64 = qleg(a, swing, 0)
106 let sFR: i64 = qleg(a, swing, offFR)
107 let sHL: i64 = qleg(a, swing, offHL)
108 let sHR: i64 = qleg(a, swing, offHR)
109 let s2: i64 = it_sin4096(a * 2)
110 let bob: i64 = bobA * s2 / QR_MAGIC_4096
111 let pitch: i64 = pitchA * it_sin4096(a) / QR_MAGIC_4096 // 1x-freq gather/extend (gallop)
112 let tsw: i64 = qdeg(14) * it_sin4096(a + QR_MAGIC_6434) / QR_MAGIC_4096
113 sk_pose(sk, QR_ROOT, 0, bob + pitch)
114 sk_pose(sk, QR_CHEST, 0, 0 - pitch / 2) // spine flexes on gallop
115 sk_pose(sk, QR_HEAD, 0, 0 - bob)
116 sk_pose(sk, QR_HIPS, 0, pitch / 2)
117 sk_pose(sk, QR_FLHIP, 0, sFL); sk_pose(sk, QR_FLKNEE, 0, kbase + qabs(sFL) / 2)
118 sk_pose(sk, QR_FRHIP, 0, sFR); sk_pose(sk, QR_FRKNEE, 0, kbase + qabs(sFR) / 2)
119 sk_pose(sk, QR_HLHIP, 0, sHL); sk_pose(sk, QR_HLKNEE, 0, kbase + qabs(sHL) / 2)
120 sk_pose(sk, QR_HRHIP, 0, sHR); sk_pose(sk, QR_HRKNEE, 0, kbase + qabs(sHR) / 2)
121 sk_pose(sk, QR_TAIL1, tsw, qdeg(10)); sk_pose(sk, QR_TAIL2, tsw, qdeg(8))
122 return 0
123}
124// add HORNS to the current quadruped body (call after quadpose_fill; n = its returned part count). Returns new n.
125func quad_add_horns(base: i64, n0: i64, sk: i64) -> i64 {
126 let orr: *i64 = sys_mmap(6 * 8) as *i64
127 var n: i64 = n0
128 orr[0]=40; orr[1]=190; orr[2]=95; orr[3]=55; orr[4]=150; orr[5]=48
129 bp_anchor(base, n, sk, QR_HEAD, orr); n = n + 1 // horn L (up+forward off the head)
130 orr[0]=40; orr[1]=190; orr[2]=0-95; orr[3]=55; orr[4]=150; orr[5]=48
131 bp_anchor(base, n, sk, QR_HEAD, orr); n = n + 1 // horn R
132 let np: *i64 = (base + O_NPART) as *i64; np[0] = n
133 sdf_clear_ops(base, n)
134 return n
135}
136
137// fill the sdfrender base with the wolf body ON the current pose. Returns part count.
138func quadpose_fill(base: i64, sk: i64) -> i64 {
139 let orr: *i64 = sys_mmap(6 * 8) as *i64
140 let o4: *i64 = sys_mmap(4 * 8) as *i64
141 var n: i64 = 0
142 // torso/head ellipsoids anchored to bones
143 orr[0]=0; orr[1]=0; orr[2]=0; orr[3]=780; orr[4]=300; orr[5]=280
144 bp_anchor(base, n, sk, QR_ROOT, orr); n = n + 1 // barrel
145 orr[0]=0; orr[1]=0; orr[2]=0; orr[3]=330; orr[4]=330; orr[5]=300
146 bp_anchor(base, n, sk, QR_CHEST, orr); n = n + 1 // chest
147 orr[0]=0; orr[1]=0; orr[2]=0; orr[3]=230; orr[4]=220; orr[5]=210
148 bp_anchor(base, n, sk, QR_HEAD, orr); n = n + 1 // head
149 orr[0]=0-260; orr[1]=0-40; orr[2]=0; orr[3]=190; orr[4]=120; orr[5]=130
150 bp_anchor(base, n, sk, QR_HEAD, orr); n = n + 1 // snout (forward -x)
151 orr[0]=0; orr[1]=0; orr[2]=0; orr[3]=300; orr[4]=270; orr[5]=260
152 bp_anchor(base, n, sk, QR_HIPS, orr); n = n + 1 // haunches
153 orr[0]=60; orr[1]=230; orr[2]=110; orr[3]=65; orr[4]=170; orr[5]=50
154 bp_anchor(base, n, sk, QR_HEAD, orr); n = n + 1 // ear L
155 orr[0]=60; orr[1]=230; orr[2]=0-110; orr[3]=65; orr[4]=170; orr[5]=50
156 bp_anchor(base, n, sk, QR_HEAD, orr); n = n + 1 // ear R
157 // legs: sphere-chains along the POSED segments (upper + lower + foot ball)
158 n = bp_chain(base, n, sk, QR_FLHIP, QR_FLKNEE, 105, 92)
159 n = bp_chain(base, n, sk, QR_FLKNEE, QR_FLFOOT, 90, 74)
160 o4[0]=0; o4[1]=0-40; o4[2]=0; o4[3]=88
161 n = bp_ball(base, n, sk, QR_FLFOOT, o4)
162 n = bp_chain(base, n, sk, QR_FRHIP, QR_FRKNEE, 105, 92)
163 n = bp_chain(base, n, sk, QR_FRKNEE, QR_FRFOOT, 90, 74)
164 o4[0]=0; o4[1]=0-40; o4[2]=0; o4[3]=88
165 n = bp_ball(base, n, sk, QR_FRFOOT, o4)
166 n = bp_chain(base, n, sk, QR_HLHIP, QR_HLKNEE, 110, 95)
167 n = bp_chain(base, n, sk, QR_HLKNEE, QR_HLFOOT, 92, 76)
168 o4[0]=0; o4[1]=0-40; o4[2]=0; o4[3]=90
169 n = bp_ball(base, n, sk, QR_HLFOOT, o4)
170 n = bp_chain(base, n, sk, QR_HRHIP, QR_HRKNEE, 110, 95)
171 n = bp_chain(base, n, sk, QR_HRKNEE, QR_HRFOOT, 92, 76)
172 o4[0]=0; o4[1]=0-40; o4[2]=0; o4[3]=90
173 n = bp_ball(base, n, sk, QR_HRFOOT, o4)
174 // tail: chain hips->tail1->tail2 + tip
175 n = bp_chain(base, n, sk, QR_HIPS, QR_TAIL1, 95, 72)
176 n = bp_chain(base, n, sk, QR_TAIL1, QR_TAIL2, 70, 56)
177 o4[0]=0; o4[1]=0; o4[2]=0; o4[3]=54
178 n = bp_ball(base, n, sk, QR_TAIL2, o4)
179 let np: *i64 = (base + O_NPART) as *i64; np[0] = n
180 let kb2: *i64 = (base + O_KBLEND) as *i64; kb2[0] = 130
181 sdf_clear_ops(base, n)
182 return n
183}