code wiki / (root) / nx_quadruped.nx

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}