nx_scene_contract.nx source
↩ module page · 186 lines · 7206 B
1// nx_scene_contract.nx -- THE style-spanning scene contract (exceed-ladder
2// R0; spec 2026-06-10-beat-top10-procgen-exceed-ladder.md).
3//
4// None of the top-10 procedural tools can take ONE character and insert it
5// into a realistic survival world, an anime world, a cartoon world and a
6// visual-novel backdrop without re-authoring assets. This contract makes
7// that a LAW of the substrate:
8//
9// identity = f(identity_seed) ONLY -- palette, build, face params.
10// style transforms PROPORTIONS and SHADING via the named tables below;
11// it must NEVER touch identity fields.
12// every style renderer consumes the SAME (*SceneWorld, *SceneEntity).
13// the entity anchors to terrain: feet on the ground in every style.
14//
15// Tiers (progressive disclosure, same as nx_procgen_preset):
16// Tier 0 nx_scene_quickstart(world_seed, human_seed, style, path) in
17// nx_scene_render.nx -- one call -> a PPM scene on disk.
18// Tier 1 nx_entity_human(seed) + nx_scene_world(seed, preset, style)
19// Tier 2 the proportion/palette tables below -- add a style = add rows.
20// license_tier: ORIGINAL
21
22import "nx_syscalls.nx"
23import "nx_tier.nx"
24import "nx_procgen_preset.nx"
25const NX_MAGIC_1103515245: i64 = 1103515245
26const NX_MAGIC_12345: i64 = 12345
27const NX_MAGIC_65536: i64 = 65536
28const NX_MAGIC_32768: i64 = 32768
29
30// ===== Sealed-enum: render styles =====================================
31const NX_STYLE_REALISTIC: nx_int = 0
32const NX_STYLE_ANIME: nx_int = 1
33const NX_STYLE_CARTOON: nx_int = 2
34const NX_STYLE_VN: nx_int = 3
35const NX_STYLE_COUNT: nx_int = 4
36
37func nx_style_is_valid(s: nx_int) -> nx_int {
38 if s < 0 { return 0 }
39 if s >= NX_STYLE_COUNT { return 0 }
40 return 1
41}
42
43// ===== SceneEntity: the synthetic human ==============================
44// 16-slot i64 record (mmap-allocated; never a stack struct).
45const NX_ENT_IDENTITY: nx_int = 0 // identity seed (provenance)
46const NX_ENT_KIND: nx_int = 1 // 0 = human (sealed for later kinds)
47const NX_ENT_HEIGHT_Q10: nx_int = 2 // canonical body height (Q10 of world relief)
48const NX_ENT_BUILD_Q10: nx_int = 3 // 768 slim .. 1280 broad (shoulder factor)
49const NX_ENT_SKIN_RGB: nx_int = 4 // 0xRRGGBB
50const NX_ENT_HAIR_RGB: nx_int = 5
51const NX_ENT_OUTFIT_RGB: nx_int = 6
52const NX_ENT_EYE_RGB: nx_int = 7
53const NX_ENT_ANCHOR_X: nx_int = 8 // world cell the feet stand on
54const NX_ENT_ANCHOR_Y: nx_int = 9
55const NX_ENT_POSE: nx_int = 10 // 0 standing (sealed for later poses)
56const NX_ENT_SLOTS: nx_int = 16
57
58// deterministic LCG over the identity seed -- same seed, same human, forever
59func _ent_lcg(state: *i64) -> nx_int {
60 state[0] = state[0] * NX_MAGIC_1103515245 + NX_MAGIC_12345
61 var v: nx_int = (state[0] / NX_MAGIC_65536) % NX_MAGIC_32768
62 if v < 0 { v = 0 - v }
63 return v
64}
65
66// identity palettes: small curated tables, indexed by seed draws. Adding
67// a row widens the population; rows are data, not logic.
68func _ent_skin(i: nx_int) -> nx_int {
69 let k: nx_int = i % 5
70 if k == 0 { return 0xF5D5B8 }
71 if k == 1 { return 0xE0B089 }
72 if k == 2 { return 0xC68642 }
73 if k == 3 { return 0x8D5524 }
74 return 0x5C3A1E
75}
76func _ent_hair(i: nx_int) -> nx_int {
77 let k: nx_int = i % 6
78 if k == 0 { return 0x101010 }
79 if k == 1 { return 0x4A2F1B }
80 if k == 2 { return 0x8B5A2B }
81 if k == 3 { return 0xD8B863 }
82 if k == 4 { return 0xB22222 }
83 return 0x7A7A7A
84}
85func _ent_outfit(i: nx_int) -> nx_int {
86 let k: nx_int = i % 6
87 if k == 0 { return 0x2E5D34 }
88 if k == 1 { return 0x27408B }
89 if k == 2 { return 0x8B2E2E }
90 if k == 3 { return 0x6A4C93 }
91 if k == 4 { return 0x3B3B3B }
92 return 0xB8860B
93}
94func _ent_eye(i: nx_int) -> nx_int {
95 let k: nx_int = i % 4
96 if k == 0 { return 0x3B2716 }
97 if k == 1 { return 0x2F5D8A }
98 if k == 2 { return 0x3E6B3E }
99 return 0x555555
100}
101
102// Tier-1: mint a synthetic human from an identity seed. EVERY field here
103// is identity -- style renderers read, never write.
104func nx_entity_human(identity_seed: nx_int) -> *i64 {
105 let e: *i64 = sys_mmap(NX_ENT_SLOTS * 8) as *i64
106 let st: *i64 = sys_mmap(16) as *i64
107 st[0] = identity_seed
108 e[NX_ENT_IDENTITY] = identity_seed
109 e[NX_ENT_KIND] = 0
110 // height: 900..1100 Q10 around the canonical 1.0
111 e[NX_ENT_HEIGHT_Q10] = 900 + (_ent_lcg(st) % 201)
112 // build: 768..1280
113 e[NX_ENT_BUILD_Q10] = 768 + (_ent_lcg(st) % 513)
114 e[NX_ENT_SKIN_RGB] = _ent_skin(_ent_lcg(st))
115 e[NX_ENT_HAIR_RGB] = _ent_hair(_ent_lcg(st))
116 e[NX_ENT_OUTFIT_RGB] = _ent_outfit(_ent_lcg(st))
117 e[NX_ENT_EYE_RGB] = _ent_eye(_ent_lcg(st))
118 e[NX_ENT_ANCHOR_X] = 0
119 e[NX_ENT_ANCHOR_Y] = 0
120 e[NX_ENT_POSE] = 0
121 return e
122}
123
124// ===== Style proportion tables (Tier-2 data) ==========================
125// head height as Q10 fraction of body height -- THE style signature.
126// realistic ~1/7.5; anime ~1/5; cartoon ~1/3; VN portrait ~1/4.5.
127func nx_style_head_ratio_q10(style: nx_int) -> nx_int {
128 if style == NX_STYLE_REALISTIC { return 137 }
129 if style == NX_STYLE_ANIME { return 205 }
130 if style == NX_STYLE_CARTOON { return 341 }
131 if style == NX_STYLE_VN { return 228 }
132 return 137
133}
134// on-screen body height in pixels (VN sprites render large)
135func nx_style_px_height(style: nx_int) -> nx_int {
136 if style == NX_STYLE_VN { return 96 }
137 if style == NX_STYLE_CARTOON { return 56 }
138 return 48
139}
140// 1 = draw a dark outline around the figure (cartoon law)
141func nx_style_outline(style: nx_int) -> nx_int {
142 if style == NX_STYLE_CARTOON { return 1 }
143 return 0
144}
145// 1 = draw eye dots (anime/VN faces read through the eyes)
146func nx_style_eyes(style: nx_int) -> nx_int {
147 if style == NX_STYLE_ANIME { return 1 }
148 if style == NX_STYLE_VN { return 1 }
149 return 0
150}
151// terrain elevation bands for cel shading (0 = continuous gradient)
152func nx_style_terrain_bands(style: nx_int) -> nx_int {
153 if style == NX_STYLE_ANIME { return 5 }
154 if style == NX_STYLE_CARTOON { return 3 }
155 return 0
156}
157
158// ===== SceneWorld =====================================================
159// 8-slot record: the landscape + the style it will be rendered under.
160const NX_SW_LAND: nx_int = 0 // *Landscape
161const NX_SW_STYLE: nx_int = 1
162const NX_SW_SEED: nx_int = 2
163const NX_SW_PRESET: nx_int = 3
164const NX_SW_SLOTS: nx_int = 8
165
166func nx_scene_world(world_seed: nx_int, preset: nx_int, style: nx_int) -> *i64 {
167 let sw: *i64 = sys_mmap(NX_SW_SLOTS * 8) as *i64
168 var st: nx_int = style
169 if nx_style_is_valid(st) == 0 { st = NX_STYLE_REALISTIC }
170 let land: *Landscape = nx_procgen_landscape(world_seed, preset,
171 NX_PROCGEN_DEFAULT_WIDTH,
172 NX_PROCGEN_DEFAULT_HEIGHT,
173 NX_PROCGEN_DEFAULT_DENSITY_Q10)
174 sw[NX_SW_LAND] = land as i64
175 sw[NX_SW_STYLE] = st
176 sw[NX_SW_SEED] = world_seed
177 sw[NX_SW_PRESET] = preset
178 return sw
179}
180
181// place the entity's feet on a world cell (the insertion point)
182func nx_scene_place(e: *i64, x: nx_int, y: nx_int) -> nx_int {
183 e[NX_ENT_ANCHOR_X] = x
184 e[NX_ENT_ANCHOR_Y] = y
185 return 0
186}