nx_scene.nx source
↩ module page · 139 lines · 5099 B
1// nx_scene.nx -- SCENE / POSITION as DATA (beat-HS2 rung 2: the Illusion-family "scene"/"position" concept,
2// on OUR moddable-data law). A scene = a set of CHANNELS, each a keyframed timeline {time, value}; sampling
3// at time t linearly interpolates (integer) the surrounding keys. Channels address anything drivable by id:
4// IK-target axes, morph weights, region params, camera. Sampled channel values feed the character stack
5// (skeleton IK targets / morph banks / region params) -- so a SCENE PACK (NXSC1) drives the whole rig, and
6// scenes flow through the CID mod economy exactly like block/morph/anatomy packs. ALL INTEGER, deterministic.
7// license_tier: ORIGINAL
8import "nx_syscalls.nx"
9const SC_MAGIC_2147483648: i64 = 2147483648
10const SC_MAGIC_4294967296: i64 = 4294967296
11
12// header: [0]=nchannels [1]=nkeys_total [2]=duration
13// channel dir at 64: 4 slots {target_id, nkeys, key_off, spare}
14// key pool after dir: 2 slots {time, value}
15const SC_MAXC: i64 = 64
16const SC_MAXK: i64 = 4096
17
18func sc_hdr(base: i64) -> *i64 { return base as *i64 }
19func sc_chan(base: i64, c: i64) -> *i64 { return (base + 64 + c * 32) as *i64 }
20func sc_key(base: i64, k: i64) -> *i64 { return (base + 64 + SC_MAXC * 32 + k * 16) as *i64 }
21func sc_bytes() -> i64 { return 64 + SC_MAXC * 32 + SC_MAXK * 16 + 64 }
22
23func sc_init(base: i64) -> i64 {
24 let h: *i64 = sc_hdr(base)
25 h[0] = 0
26 h[1] = 0
27 h[2] = 0
28 return 0
29}
30func sc_channel(base: i64, target_id: i64) -> i64 {
31 let h: *i64 = sc_hdr(base)
32 if h[0] >= SC_MAXC { return 0 - 1 }
33 let c: *i64 = sc_chan(base, h[0])
34 c[0] = target_id
35 c[1] = 0
36 c[2] = h[1]
37 h[0] = h[0] + 1
38 return h[0] - 1
39}
40// keys MUST be added in ascending time per channel (the sampler assumes sorted)
41func sc_key_add(base: i64, chan: i64, time: i64, value: i64) -> i64 {
42 let h: *i64 = sc_hdr(base)
43 if h[1] >= SC_MAXK { return 0 - 1 }
44 let k: *i64 = sc_key(base, h[1])
45 k[0] = time
46 k[1] = value
47 let c: *i64 = sc_chan(base, chan)
48 c[1] = c[1] + 1
49 h[1] = h[1] + 1
50 if time > h[2] { h[2] = time }
51 return 0
52}
53func sc_target(base: i64, chan: i64) -> i64 { let c: *i64 = sc_chan(base, chan); return c[0] }
54func sc_duration(base: i64) -> i64 { let h: *i64 = sc_hdr(base); return h[2] }
55
56// sample channel `chan` at time t: clamp before-first/after-last, else linear-interp the bracketing keys
57func sc_sample(base: i64, chan: i64, t: i64) -> i64 {
58 let c: *i64 = sc_chan(base, chan)
59 let n: i64 = c[1]
60 let off: i64 = c[2]
61 if n == 0 { return 0 }
62 let first: *i64 = sc_key(base, off)
63 if t <= first[0] { return first[1] }
64 let last: *i64 = sc_key(base, off + n - 1)
65 if t >= last[0] { return last[1] }
66 var i: i64 = 0
67 while i < n - 1 {
68 let ka: *i64 = sc_key(base, off + i)
69 let kb: *i64 = sc_key(base, off + i + 1)
70 if t >= ka[0] { if t <= kb[0] {
71 let span: i64 = kb[0] - ka[0]
72 if span == 0 { return ka[1] }
73 return ka[1] + (kb[1] - ka[1]) * (t - ka[0]) / span
74 } }
75 i = i + 1
76 }
77 return last[1]
78}
79
80// ---- NXSC1 pack: "NXSC" + nchan + dur + per-channel {target,nkeys}{keys...} as i32 LE ----
81func sc_w32(buf: *u8, off: i64, v: i64) -> i64 {
82 buf[off] = (v & 255) as u8
83 buf[off + 1] = ((v >> 8) & 255) as u8
84 buf[off + 2] = ((v >> 16) & 255) as u8
85 buf[off + 3] = ((v >> 24) & 255) as u8
86 return 0
87}
88func sc_r32(buf: *u8, off: i64) -> i64 {
89 var v: i64 = (buf[off] as i64) | ((buf[off + 1] as i64) << 8) | ((buf[off + 2] as i64) << 16) | ((buf[off + 3] as i64) << 24)
90 if v >= SC_MAGIC_2147483648 { v = v - SC_MAGIC_4294967296 }
91 return v
92}
93func sc_save(base: i64, buf: *u8) -> i64 {
94 let h: *i64 = sc_hdr(base)
95 buf[0] = 78 as u8; buf[1] = 88 as u8; buf[2] = 83 as u8; buf[3] = 67 as u8 // "NXSC"
96 sc_w32(buf, 4, h[0])
97 sc_w32(buf, 8, h[2])
98 var p: i64 = 12
99 var c: i64 = 0
100 while c < h[0] {
101 let ch: *i64 = sc_chan(base, c)
102 sc_w32(buf, p, ch[0]); sc_w32(buf, p + 4, ch[1]); p = p + 8
103 var k: i64 = 0
104 while k < ch[1] {
105 let key: *i64 = sc_key(base, ch[2] + k)
106 sc_w32(buf, p, key[0]); sc_w32(buf, p + 4, key[1]); p = p + 8
107 k = k + 1
108 }
109 c = c + 1
110 }
111 return p
112}
113func sc_load(base: i64, buf: *u8, len: i64) -> i64 {
114 if len < 12 { return 0 - 1 }
115 if buf[0] as i64 != 78 { return 0 - 1 }
116 if buf[1] as i64 != 88 { return 0 - 1 }
117 if buf[2] as i64 != 83 { return 0 - 1 }
118 if buf[3] as i64 != 67 { return 0 - 1 }
119 sc_init(base)
120 let nc: i64 = sc_r32(buf, 4)
121 var p: i64 = 12
122 var c: i64 = 0
123 while c < nc {
124 if p + 8 > len { return 0 - 1 }
125 let target: i64 = sc_r32(buf, p)
126 let nk: i64 = sc_r32(buf, p + 4)
127 p = p + 8
128 let ci: i64 = sc_channel(base, target)
129 var k: i64 = 0
130 while k < nk {
131 if p + 8 > len { return 0 - 1 }
132 sc_key_add(base, ci, sc_r32(buf, p), sc_r32(buf, p + 4))
133 p = p + 8
134 k = k + 1
135 }
136 c = c + 1
137 }
138 return nc
139}