nx_assetvariant.nx source
↩ module page · 135 lines · 6663 B
1// nx_assetvariant.nx -- SWAP-AS-DATA v0 (swap+motion census SCOPE A, the DnD/game ask): reuse ONE synthetic
2// base asset (person / wolf / goblin) and produce MANY variants by SWAPPING slots -- head (plain/horns/snout),
3// ears (none/pointy), tail (none/tail), build (slim/normal/bulk radius scale). A VARIANT IS DATA: the tuple
4// {species, head, ears, tail, build} fully determines the SDF part list, so a bestiary row = a tuple, and
5// diversity = enumerating tuples -- no hand-crafted one-offs. Fills the nx_sdfrender base (parts/npart/kblend)
6// exactly like sdf_body/t2mesh, so every variant renders on the SAME photoreal-path shader and can feed
7// nx_meshgen -> STL for game assets. Neural face/clothes swap on IMAGES = the S5 rung; this is the parametric
8// spine (like t2mesh before Shap-E). license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "nx_sdfrender.nx"
11const AV_MAGIC_1250: i64 = 1250
12const AV_MAGIC_1060: i64 = 1060
13const AV_MAGIC_1190: i64 = 1190
14
15// species ids
16const AV_HUMAN: i64 = 0
17const AV_WOLF: i64 = 1
18const AV_GOBLIN: i64 = 2
19
20func av_p(base: i64, ip: *i64, cx: i64, cy: i64, cz: i64, rx: i64, ry: i64, rz: i64) -> i64 {
21 let p: *i64 = (base + O_PARTS) as *i64
22 let i: i64 = ip[0]
23 p[i * 6] = cx; p[i * 6 + 1] = cy; p[i * 6 + 2] = cz
24 p[i * 6 + 3] = rx; p[i * 6 + 4] = ry; p[i * 6 + 5] = rz
25 ip[0] = i + 1
26 return 0
27}
28
29// --- species bases (part recipes; grounded near the same world scale as sdf_body, camera camz=5) ---------
30func av_base_human(base: i64, ip: *i64) -> i64 {
31 // compact humanoid (reuses sdf_body proportions, fewer parts so slots read clearly)
32 av_p(base, ip, 0, AV_MAGIC_1250, 0, 225, 285, 235) // head
33 av_p(base, ip, 0, 1010, 0, 115, 160, 115) // neck
34 av_p(base, ip, 0, 770, 0, 330, 300, 235) // chest
35 av_p(base, ip, 0, 500, 0, 250, 250, 200) // waist
36 av_p(base, ip, 0, 80, 0, 410, 280, 270) // hips
37 av_p(base, ip, 0-470, 650, 0, 145, 340, 145) // arm L up
38 av_p(base, ip, 0-620, 280, 0, 115, 330, 118) // arm L lo
39 av_p(base, ip, 470, 650, 0, 145, 340, 145) // arm R up
40 av_p(base, ip, 620, 280, 0, 115, 330, 118) // arm R lo
41 av_p(base, ip, 0-195, 0-520, 0, 205, 640, 205) // leg L
42 av_p(base, ip, 195, 0-520, 0, 205, 640, 205) // leg R
43 return AV_MAGIC_1250 // head center Y (slot anchor)
44}
45func av_base_wolf(base: i64, ip: *i64) -> i64 {
46 // quadruped: long horizontal barrel + chest + head w/ snout-anchor forward + 4 legs
47 av_p(base, ip, 0, 0-100, 0, 780, 300, 280) // body barrel (long in x)
48 av_p(base, ip, 0-560, 20, 0, 330, 330, 300) // chest/shoulders
49 av_p(base, ip, 0-940, 320, 0, 230, 220, 210) // head
50 av_p(base, ip, 560, 0-40, 0, 300, 270, 260) // haunches
51 av_p(base, ip, 0-560, 0-640, 90, 95, 420, 95) // front leg L(z+)
52 av_p(base, ip, 0-560, 0-640, 0-90, 95, 420, 95) // front leg R
53 av_p(base, ip, 560, 0-640, 90, 100, 420, 100) // hind leg L
54 av_p(base, ip, 560, 0-640, 0-90, 100, 420, 100) // hind leg R
55 return 320 // head center Y
56}
57func av_base_goblin(base: i64, ip: *i64) -> i64 {
58 // short humanoid: big head, squat torso, short legs
59 av_p(base, ip, 0, 820, 0, 300, 330, 300) // BIG head
60 av_p(base, ip, 0, 500, 0, 105, 130, 105) // neck
61 av_p(base, ip, 0, 280, 0, 330, 280, 240) // torso
62 av_p(base, ip, 0, 0-30, 0, 340, 240, 250) // belly/hips
63 av_p(base, ip, 0-420, 260, 0, 120, 300, 120) // arm L
64 av_p(base, ip, 420, 260, 0, 120, 300, 120) // arm R
65 av_p(base, ip, 0-170, 0-480, 0, 150, 430, 150) // leg L
66 av_p(base, ip, 170, 0-480, 0, 150, 430, 150) // leg R
67 return 820 // head center Y
68}
69
70// --- swap slots (each writes parts relative to the species head anchor / body) ---------------------------
71func av_slot_head(base: i64, ip: *i64, sp: i64, hv: i64, heady: i64) -> i64 {
72 var hx: i64 = 0
73 if sp == AV_WOLF { hx = 0 - 940 }
74 if hv == 1 { // HORNS: two cones-ish above the head
75 av_p(base, ip, hx - 130, heady + 300, 0, 60, 220, 60)
76 av_p(base, ip, hx + 130, heady + 300, 0, 60, 220, 60)
77 }
78 if hv == 2 { // SNOUT: forward muzzle (z+ for bipeds, x- for wolf)
79 if sp == AV_WOLF { av_p(base, ip, hx - 260, heady - 40, 0, 190, 120, 130) }
80 else { av_p(base, ip, hx, heady - 40, 240, 130, 110, 190) }
81 }
82 return 0
83}
84func av_slot_ears(base: i64, ip: *i64, sp: i64, ev: i64, heady: i64) -> i64 {
85 if ev == 0 { return 0 }
86 var hx: i64 = 0
87 if sp == AV_WOLF { hx = 0 - 940 }
88 var ry: i64 = 220
89 if sp == AV_GOBLIN { ry = 300 } // goblin ears BIG
90 av_p(base, ip, hx - 210, heady + 210, 0, 70, ry, 55)
91 av_p(base, ip, hx + 210, heady + 210, 0, 70, ry, 55)
92 return 0
93}
94func av_slot_tail(base: i64, ip: *i64, sp: i64, tv: i64) -> i64 {
95 if tv == 0 { return 0 }
96 if sp == AV_WOLF {
97 av_p(base, ip, 900, 200, 0, 90, 90, 90)
98 av_p(base, ip, AV_MAGIC_1060, 360, 0, 80, 80, 80)
99 av_p(base, ip, AV_MAGIC_1190, 540, 0, 70, 70, 70)
100 } else {
101 av_p(base, ip, 0, 0-60, 0-320, 80, 80, 80)
102 av_p(base, ip, 0, 0-180, 0-470, 70, 70, 70)
103 av_p(base, ip, 0, 0-340, 0-560, 60, 60, 60)
104 }
105 return 0
106}
107
108// build a full variant into base: tuple {species, head, ears, tail, buildpct}. Returns part count.
109func av_build(base: i64, sp: i64, hv: i64, ev: i64, tv: i64, buildpct: i64) -> i64 {
110 let ip: *i64 = sys_mmap(8) as *i64
111 ip[0] = 0
112 var heady: i64 = 0
113 if sp == AV_HUMAN { heady = av_base_human(base, ip) }
114 if sp == AV_WOLF { heady = av_base_wolf(base, ip) }
115 if sp == AV_GOBLIN { heady = av_base_goblin(base, ip) }
116 av_slot_head(base, ip, sp, hv, heady)
117 av_slot_ears(base, ip, sp, ev, heady)
118 av_slot_tail(base, ip, sp, tv)
119 let n: i64 = ip[0]
120 // BUILD slot: scale every radius (slim 80 / normal 100 / bulk 125)
121 if buildpct != 100 {
122 let p: *i64 = (base + O_PARTS) as *i64
123 var i: i64 = 0
124 while i < n {
125 p[i * 6 + 3] = p[i * 6 + 3] * buildpct / 100
126 p[i * 6 + 4] = p[i * 6 + 4] * buildpct / 100
127 p[i * 6 + 5] = p[i * 6 + 5] * buildpct / 100
128 i = i + 1
129 }
130 }
131 let np: *i64 = (base + O_NPART) as *i64; np[0] = n
132 let kb: *i64 = (base + O_KBLEND) as *i64; kb[0] = 130
133 sdf_clear_ops(base, n)
134 return n
135}