nx_treegen.nx source
↩ module page · 324 lines · 17528 B
1// nx_treegen.nx -- ★SOVEREIGN INFINIGEN: procedural VEGETATION (trees). A seed grows a UNIQUE tree by recursive
2// branching (an L-system-style growth stack): trunk -> branches -> sub-branches -> leaf clusters, with
3// phototropism (upward bias) + seeded angle/length/count variation. Branches = tapering CAPSULES, leaves = green
4// spheres. Self-contained SDF + integer ray-march with a bounding-sphere early-reject (so hundreds of parts stay
5// fast). No ML, no assets. Different seed -> different tree. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_itrig.nx"
8const TG_MAGIC_6364136223846793005: i64 = 6364136223846793005
9const TG_MAGIC_1442695040888963407: i64 = 1442695040888963407
10const TG_MAGIC_2147483647: i64 = 2147483647
11const TG_MAGIC_1024: i64 = 1024
12const TG_MAGIC_1048576: i64 = 1048576
13const TG_MAGIC_65536: i64 = 65536
14const TG_MAGIC_2654435761: i64 = 2654435761
15const TG_MAGIC_1013904223: i64 = 1013904223
16const TG_MAGIC_7919: i64 = 7919
17const TG_MAGIC_1700: i64 = 1700
18const TG_MAGIC_2900: i64 = 2900
19const TG_MAGIC_1400: i64 = 1400
20const TG_MAGIC_2400: i64 = 2400
21const TG_MAGIC_2100: i64 = 2100
22const TG_MAGIC_3400: i64 = 3400
23const TG_MAGIC_4096: i64 = 4096
24const TG_MAGIC_25736: i64 = 25736
25const TG_MAGIC_2600: i64 = 2600
26const TG_MAGIC_1500: i64 = 1500
27const TG_MAGIC_25735: i64 = 25735
28const TG_MAGIC_4090: i64 = 4090
29const TG_MAGIC_1000000: i64 = 1000000
30const TG_MAGIC_1100: i64 = 1100
31const TG_MAGIC_1103515245: i64 = 1103515245
32const TG_MAGIC_12345: i64 = 12345
33
34const TG_CAP: i64 = 700 // max parts
35const TG_STR: i64 = 14 // per-part: type,kind, cx,cy,cz, bound, p1(3), r, p2(3)
36const TG_CAPS: i64 = 0
37const TG_SPH: i64 = 1
38const TG_WOOD: i64 = 0
39const TG_LEAF: i64 = 1
40const TG_W: i64 = 448
41const TG_H: i64 = 512
42const TG_FOCAL: i64 = 620
43
44func tg_isqrt(v: i64) -> i64 { if v<=0 {return 0} var x: i64=v; var y: i64=(x+1)/2; while y<x {x=y; y=(x+v/x)/2} return x }
45func tg_abs(v: i64) -> i64 { if v<0 {return 0-v} return v }
46func tg_min(a: i64, b: i64) -> i64 { if a<b {return a} return b }
47func tg_max(a: i64, b: i64) -> i64 { if a>b {return a} return b }
48func tg_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v<lo {return lo} if v>hi {return hi} return v }
49func tg_next(st: *i64) -> i64 { var h: i64=st[0]; h=h*TG_MAGIC_6364136223846793005+TG_MAGIC_1442695040888963407; st[0]=h; return (h>>33)&TG_MAGIC_2147483647 }
50func tg_range(st: *i64, lo: i64, hi: i64) -> i64 { return lo + tg_next(st) % (hi-lo+1) }
51
52func tg_addcaps(P: *i64, n: i64, ax: i64, ay: i64, az: i64, bx: i64, by: i64, bz: i64, r: i64, kind: i64) -> i64 {
53 let i: i64 = n*TG_STR
54 P[i]=TG_CAPS; P[i+1]=kind; P[i+2]=(ax+bx)/2; P[i+3]=(ay+by)/2; P[i+4]=(az+bz)/2
55 let hl: i64 = tg_isqrt((bx-ax)*(bx-ax)+(by-ay)*(by-ay)+(bz-az)*(bz-az))/2
56 P[i+5]=hl+r; P[i+6]=ax; P[i+7]=ay; P[i+8]=az; P[i+9]=r; P[i+10]=bx; P[i+11]=by; P[i+12]=bz; P[i+13]=0
57 return 0
58}
59func tg_addsph(P: *i64, n: i64, cx: i64, cy: i64, cz: i64, r: i64, kind: i64) -> i64 {
60 let i: i64 = n*TG_STR
61 P[i]=TG_SPH; P[i+1]=kind; P[i+2]=cx; P[i+3]=cy; P[i+4]=cz; P[i+5]=r; P[i+6]=cx; P[i+7]=cy; P[i+8]=cz; P[i+9]=r; P[i+13]=0
62 return 0
63}
64
65// tilt a unit(fx1024) direction by polar `ang` around azimuth `phi` (both it4096), + upward bias, renormalize
66func tg_tilt(dx: i64, dy: i64, dz: i64, ang: i64, phi: i64, out: *i64) -> i64 {
67 // u = cross(dir, up) with up=(0,1024,0) = (-dz*1024, 0, dx*1024)
68 var ux: i64 = 0 - dz*TG_MAGIC_1024; var uy: i64 = 0; var uz: i64 = dx*TG_MAGIC_1024
69 var ul: i64 = tg_isqrt(ux*ux+uy*uy+uz*uz)
70 if ul < 64 { ux=TG_MAGIC_1048576; uy=0; uz=0; ul=TG_MAGIC_1048576 } // dir ~ vertical -> use x axis (TG_MAGIC_1024*TG_MAGIC_1024 scale)
71 ux=ux*TG_MAGIC_1024/ul; uy=uy*TG_MAGIC_1024/ul; uz=uz*TG_MAGIC_1024/ul
72 // v = cross(dir,u)
73 var vx: i64 = (dy*uz - dz*uy)/TG_MAGIC_1024; var vy: i64 = (dz*ux - dx*uz)/TG_MAGIC_1024; var vz: i64 = (dx*uy - dy*ux)/TG_MAGIC_1024
74 let ca: i64 = it_cos4096(ang)/4; let sa: i64 = it_sin4096(ang)/4 // fx1024
75 let cp: i64 = it_cos4096(phi)/4; let sp: i64 = it_sin4096(phi)/4
76 let perpx: i64 = ux*cp/TG_MAGIC_1024 + vx*sp/TG_MAGIC_1024
77 let perpy: i64 = uy*cp/TG_MAGIC_1024 + vy*sp/TG_MAGIC_1024
78 let perpz: i64 = uz*cp/TG_MAGIC_1024 + vz*sp/TG_MAGIC_1024
79 var cxx: i64 = dx*ca/TG_MAGIC_1024 + perpx*sa/TG_MAGIC_1024
80 var cyy: i64 = dy*ca/TG_MAGIC_1024 + perpy*sa/TG_MAGIC_1024 + 210 // phototropism (upward bias)
81 var czz: i64 = dz*ca/TG_MAGIC_1024 + perpz*sa/TG_MAGIC_1024
82 let l: i64 = tg_isqrt(cxx*cxx+cyy*cyy+czz*czz)
83 if l>0 { cxx=cxx*TG_MAGIC_1024/l; cyy=cyy*TG_MAGIC_1024/l; czz=czz*TG_MAGIC_1024/l }
84 out[0]=cxx; out[1]=cyy; out[2]=czz
85 return 0
86}
87
88// ★R1 DIVERSITY: SPECIES GRAMMAR (the census critic's bar: "ONE tree grammar vs their species systems"). A
89// species = a parameter row over the SAME growth grammar (angles, decay, depth, leaf size/count, palettes):
90// 0 BROADLEAF the original umbrella-canopy tree
91// 1 CONIFER narrow: small branch angles, strong decay, deep, small dark clusters -> spire silhouette
92// 2 BIRCH pale bark, small bright leaves, slender, more branches
93// 3 PALM bare tall trunk (depth 0 sides), one crown of LONG frond blobs
94// 4 DEAD no leaves at all, grey wood, gnarled wide angles
95static TG_WOODRGB: i64 // per-species palettes read by treegen_render (0 -> defaults)
96static TG_LEAFRGB: i64
97func tg_species_palette(species: i64) -> i64 {
98 TG_WOODRGB = 96 + 66*256 + 40*TG_MAGIC_65536 // broadleaf default
99 TG_LEAFRGB = 46 + 88*256 + 36*TG_MAGIC_65536
100 if species == 1 { TG_WOODRGB = 78 + 54*256 + 36*TG_MAGIC_65536; TG_LEAFRGB = 26 + 62*256 + 30*TG_MAGIC_65536 } // conifer: dark
101 if species == 2 { TG_WOODRGB = 196 + 190*256 + 178*TG_MAGIC_65536; TG_LEAFRGB = 88 + 132*256 + 44*TG_MAGIC_65536 } // birch: pale bark
102 if species == 3 { TG_WOODRGB = 122 + 96*256 + 62*TG_MAGIC_65536; TG_LEAFRGB = 60 + 118*256 + 44*TG_MAGIC_65536 } // palm
103 if species == 4 { TG_WOODRGB = 92 + 88*256 + 84*TG_MAGIC_65536; TG_LEAFRGB = 0 } // dead: grey, leafless
104 return 0
105}
106
107// grow a tree of a SPECIES from a seed. base at (0,-380,0), grows +y. Returns part count.
108func treegen_build_sp(P: *i64, seed: i64, species: i64) -> i64 {
109 tg_species_palette(species)
110 let st: *i64 = sys_mmap(16) as *i64
111 st[0] = seed*TG_MAGIC_2654435761 + TG_MAGIC_1013904223 + species*TG_MAGIC_7919; tg_next(st); tg_next(st)
112 // species parameter row: trunk len/rad, depth, branch angle lo/hi, count lo/hi, len-decay, leaf size lo/hi, leafn
113 var tlen: i64 = tg_range(st,235,285)
114 var trad: i64 = tg_range(st,24,30)
115 var dep0: i64 = 5
116 var anglo: i64 = TG_MAGIC_1700
117 var anghi: i64 = TG_MAGIC_2900
118 var chlo: i64 = 2
119 var chhi: i64 = 3
120 var decay: i64 = 73
121 var lflo: i64 = 22
122 var lfhi: i64 = 38
123 var leafn: i64 = 6
124 var ljx: i64 = 62 // leaf jitter x/z
125 var ljyu: i64 = 74 // leaf jitter y up
126 if species == 1 { dep0=0; tlen=tg_range(st,380,460); trad=tg_range(st,20,26) } // conifer: dedicated whorl build below
127 if species == 2 { dep0=5; anglo=TG_MAGIC_1400; anghi=TG_MAGIC_2400; chlo=2; chhi=4; decay=70; lflo=14; lfhi=22; leafn=7; ljx=52; ljyu=60; trad=tg_range(st,16,20) }
128 if species == 3 { dep0=0; tlen=tg_range(st,340,420); trad=tg_range(st,20,26) } // palm: trunk only
129 if species == 4 { dep0=5; anglo=TG_MAGIC_2100; anghi=TG_MAGIC_3400; chlo=2; chhi=3; decay=76; leafn=0 } // dead: no leaves
130 let S: *i64 = sys_mmap(TG_MAGIC_4096*9*8) as *i64 // growth stack: x,y,z,dx,dy,dz,len,rad,depth
131 var sp: i64 = 0
132 S[0]=0; S[1]=0-380; S[2]=0; S[3]=0; S[4]=TG_MAGIC_1024; S[5]=0; S[6]=tlen; S[7]=trad; S[8]=dep0
133 sp = 1
134 let od: *i64 = sys_mmap(32) as *i64
135 var np: i64 = 0
136 while sp > 0 {
137 sp = sp - 1
138 let bx: i64 = S[sp*9]; let by: i64 = S[sp*9+1]; let bz: i64 = S[sp*9+2]
139 let dx: i64 = S[sp*9+3]; let dy: i64 = S[sp*9+4]; let dz: i64 = S[sp*9+5]
140 let ln: i64 = S[sp*9+6]; let rad: i64 = S[sp*9+7]; let dep: i64 = S[sp*9+8]
141 let ex: i64 = bx + dx*ln/TG_MAGIC_1024; let ey: i64 = by + dy*ln/TG_MAGIC_1024; let ez: i64 = bz + dz*ln/TG_MAGIC_1024
142 if np < TG_CAP { tg_addcaps(P, np, bx, by, bz, ex, ey, ez, rad, TG_WOOD); np = np + 1 }
143 if dep == 0 {
144 if species == 1 {
145 // CONIFER: branch WHORLS down the trunk, longer at the bottom -> the spruce cone silhouette
146 var lev: i64 = 0
147 while lev < 7 {
148 let wy: i64 = by + ln*(22 + lev*11)/100 // whorl height up the trunk
149 let wlen: i64 = ln*(46 - lev*6)/100 // branch length shrinks upward
150 var wb: i64 = 0
151 while wb < 5 {
152 let wphi: i64 = wb*TG_MAGIC_25736/5 + lev*TG_MAGIC_2600 + tg_range(st,0,TG_MAGIC_1400)
153 let wdx: i64 = it_cos4096(wphi)/4
154 let wdz: i64 = it_sin4096(wphi)/4
155 let bex: i64 = bx + wdx*wlen/TG_MAGIC_1024
156 let bey: i64 = wy - wlen/5 // branches slope DOWN
157 let bez: i64 = bz + wdz*wlen/TG_MAGIC_1024
158 if np < TG_CAP { tg_addcaps(P, np, bx, wy, bz, bex, bey, bez, 7, TG_WOOD); np = np + 1 }
159 // needle blobs along the outer 2/3 of the branch
160 var ns: i64 = 0
161 while ns < 3 {
162 let t2: i64 = 400 + ns*250
163 if np < TG_CAP { tg_addsph(P, np, bx + wdx*wlen*t2/TG_MAGIC_1048576, wy - wlen*t2/5/TG_MAGIC_1024, bz + wdz*wlen*t2/TG_MAGIC_1048576, 20 - lev, TG_LEAF); np = np + 1 }
164 ns = ns + 1
165 }
166 wb = wb + 1
167 }
168 lev = lev + 1
169 }
170 // crown tip
171 if np < TG_CAP { tg_addsph(P, np, bx, by + ln + 16, bz, 20, TG_LEAF); np = np + 1 }
172 }
173 if species == 3 {
174 // palm crown: a ring of LONG radial frond blobs from the trunk top
175 var fr: i64 = 0
176 while fr < 8 {
177 let fphi: i64 = fr*TG_MAGIC_25736/8 + tg_range(st,0,TG_MAGIC_1500)
178 let fdx: i64 = it_cos4096(fphi)/4
179 let fdz: i64 = it_sin4096(fphi)/4
180 var seg: i64 = 0
181 while seg < 6 {
182 let fd: i64 = 44 + seg*52
183 let droop: i64 = seg*seg*11
184 if np < TG_CAP { tg_addsph(P, np, ex + fdx*fd/TG_MAGIC_1024, ey + 30 - droop, ez + fdz*fd/TG_MAGIC_1024, 30 - seg*3, TG_LEAF); np = np + 1 }
185 seg = seg + 1
186 }
187 fr = fr + 1
188 }
189 }
190 if species != 1 { if species != 3 {
191 var lf: i64 = 0
192 while lf < leafn {
193 let lr: i64 = tg_range(st, lflo, lfhi)
194 if np < TG_CAP { tg_addsph(P, np, ex + tg_range(st,0-ljx,ljx), ey + tg_range(st,0-24,ljyu), ez + tg_range(st,0-ljx,ljx), lr, TG_LEAF); np = np + 1 }
195 lf = lf + 1
196 }
197 } }
198 } else {
199 let nch: i64 = tg_range(st, chlo, chhi)
200 var c: i64 = 0
201 while c < nch {
202 let phi: i64 = tg_range(st, 0, TG_MAGIC_25735)
203 let ang: i64 = tg_range(st, anglo, anghi)
204 tg_tilt(dx, dy, dz, ang, phi, od)
205 if sp < TG_MAGIC_4090 {
206 S[sp*9]=ex; S[sp*9+1]=ey; S[sp*9+2]=ez; S[sp*9+3]=od[0]; S[sp*9+4]=od[1]; S[sp*9+5]=od[2]
207 S[sp*9+6]=ln*tg_range(st,decay-5,decay+5)/100; S[sp*9+7]=rad*66/100; S[sp*9+8]=dep-1
208 sp = sp + 1
209 }
210 c = c + 1
211 }
212 }
213 }
214 return np
215}
216
217// back-compat: the original broadleaf
218func treegen_build(P: *i64, seed: i64) -> i64 { return treegen_build_sp(P, seed, 0) }
219
220func tg_part_sdf(P: *i64, k: i64, x: i64, y: i64, z: i64) -> i64 {
221 let i: i64 = k*TG_STR
222 if P[i]==TG_SPH { let dx: i64=x-P[i+2]; let dy: i64=y-P[i+3]; let dz: i64=z-P[i+4]; return tg_isqrt(dx*dx+dy*dy+dz*dz)-P[i+9] }
223 let ax: i64=P[i+6]; let ay: i64=P[i+7]; let az: i64=P[i+8]; let r: i64=P[i+9]
224 let bx: i64=P[i+10]; let by: i64=P[i+11]; let bz: i64=P[i+12]
225 let pax: i64=x-ax; let pay: i64=y-ay; let paz: i64=z-az
226 let bax: i64=bx-ax; let bay: i64=by-ay; let baz: i64=bz-az
227 let bb: i64=bax*bax+bay*bay+baz*baz
228 var h: i64=0
229 if bb>0 { h=tg_clamp((pax*bax+pay*bay+paz*baz)*TG_MAGIC_1024/bb,0,TG_MAGIC_1024) }
230 let cx: i64=pax-bax*h/TG_MAGIC_1024; let cy: i64=pay-bay*h/TG_MAGIC_1024; let cz: i64=paz-baz*h/TG_MAGIC_1024
231 return tg_isqrt(cx*cx+cy*cy+cz*cz)-r
232}
233// min-union with bounding-sphere reject; sets kindout[0] to the kind and kindout[1] to the INDEX of the nearest
234// part (the index seeds per-leaf colour jitter -- canopy detail)
235func tg_sdf(P: *i64, np: i64, x: i64, y: i64, z: i64, kindout: *i64) -> i64 {
236 var d: i64 = TG_MAGIC_1000000; var kind: i64 = 0
237 var kidx: i64 = 0
238 var k: i64 = 0
239 while k < np {
240 let i: i64 = k*TG_STR
241 let dcx: i64=x-P[i+2]; let dcy: i64=y-P[i+3]; let dcz: i64=z-P[i+4]
242 let cd2: i64 = dcx*dcx+dcy*dcy+dcz*dcz
243 let thr: i64 = d + P[i+5]
244 var skip: i64 = 0
245 if thr > 0 { if cd2 > thr*thr { skip = 1 } }
246 if skip == 0 {
247 let pd: i64 = tg_part_sdf(P, k, x, y, z)
248 if pd < d { d = pd; kind = P[i+1]; kidx = k }
249 }
250 k = k + 1
251 }
252 kindout[0] = kind
253 kindout[1] = kidx
254 return d
255}
256
257func treegen_render(P: *i64, np: i64, yaw: i64, camz: i64, bgr: i64, bgg: i64, bgb: i64, fb: *i64) -> i64 {
258 let sy4: i64 = it_sin4096(yaw); let cy4: i64 = it_cos4096(yaw)
259 let R: i64 = camz*TG_MAGIC_1024
260 let ox: i64 = 0 - sy4*R/TG_MAGIC_4096; let oz: i64 = 0 - cy4*R/TG_MAGIC_4096
261 let lx: i64 = 320; let ly: i64 = 640; let lz: i64 = 0-680 // upper-RIGHT-front = matches the worldgen sun
262 let ll: i64 = tg_isqrt(lx*lx+ly*ly+lz*lz)
263 let ko: *i64 = sys_mmap(16) as *i64
264 var py: i64 = 0
265 while py < TG_H {
266 var px: i64 = 0
267 while px < TG_W {
268 let ndcx: i64 = px - TG_W/2
269 let ndcy: i64 = TG_H/2 - py
270 var dx: i64 = sy4*TG_FOCAL/TG_MAGIC_4096 + cy4*ndcx/TG_MAGIC_4096
271 var dy: i64 = ndcy
272 var dz: i64 = cy4*TG_FOCAL/TG_MAGIC_4096 - sy4*ndcx/TG_MAGIC_4096
273 let dl: i64 = tg_isqrt(dx*dx+dy*dy+dz*dz)
274 if dl>0 { dx=dx*TG_MAGIC_1024/dl; dy=dy*TG_MAGIC_1024/dl; dz=dz*TG_MAGIC_1024/dl }
275 var t: i64 = R - TG_MAGIC_1100
276 if t<0 {t=0}
277 var hit: i64 = 0; var hx: i64=0; var hy: i64=0; var hz: i64=0; var hk: i64=0; var hki: i64=0
278 var step: i64 = 0
279 while step < 110 {
280 let sx: i64 = ox + dx*t/TG_MAGIC_1024; let sy: i64 = dy*t/TG_MAGIC_1024; let sz: i64 = oz + dz*t/TG_MAGIC_1024
281 let d: i64 = tg_sdf(P, np, sx, sy, sz, ko)
282 if d < 2 { hit=1; hx=sx; hy=sy; hz=sz; hk=ko[0]; hki=ko[1]; step=999 }
283 else { t = t + d*8/10; if t > R+TG_MAGIC_1100 { step=999 } }
284 if step != 999 { step = step + 1 }
285 }
286 var col: i64 = bgr + bgg*256 + bgb*TG_MAGIC_65536
287 if hit == 1 {
288 let e: i64 = 3
289 let gx: i64 = tg_sdf(P,np,hx+e,hy,hz,ko)-tg_sdf(P,np,hx-e,hy,hz,ko)
290 let gy: i64 = tg_sdf(P,np,hx,hy+e,hz,ko)-tg_sdf(P,np,hx,hy-e,hz,ko)
291 let gz: i64 = tg_sdf(P,np,hx,hy,hz+e,ko)-tg_sdf(P,np,hx,hy,hz-e,ko)
292 let gl: i64 = tg_isqrt(gx*gx+gy*gy+gz*gz)
293 var nd: i64 = 0
294 if gl>0 { nd=(gx*lx+gy*ly+gz*lz)*TG_MAGIC_1024/(gl*ll) }
295 if nd<0 {nd=0}
296 let lit: i64 = 46 + nd*74/TG_MAGIC_1024
297 // species palettes (statics set by tg_species_palette; 0 -> broadleaf defaults)
298 var wrgb: i64 = TG_WOODRGB
299 if wrgb == 0 { wrgb = 96 + 66*256 + 40*TG_MAGIC_65536 }
300 var lrgb: i64 = TG_LEAFRGB
301 if lrgb == 0 { lrgb = 46 + 88*256 + 36*TG_MAGIC_65536 }
302 var cr: i64 = wrgb & 255
303 var cg: i64 = (wrgb >> 8) & 255
304 var cb: i64 = (wrgb >> 16) & 255
305 if hk == TG_LEAF {
306 // per-leaf colour jitter (seeded by the part index) -> a varied, detailed canopy
307 var j2: i64 = hki*TG_MAGIC_1103515245 + TG_MAGIC_12345
308 if j2 < 0 { j2 = 0 - j2 }
309 cr = (lrgb & 255) + (j2 % 23)
310 cg = ((lrgb >> 8) & 255) + ((j2/23) % 37)
311 cb = ((lrgb >> 16) & 255) + ((j2/851) % 17)
312 }
313 var r: i64 = cr*lit/100; var g: i64 = cg*lit/100; var b: i64 = cb*lit/100
314 col = tg_clamp(r,0,255) + tg_clamp(g,0,255)*256 + tg_clamp(b,0,255)*TG_MAGIC_65536
315 }
316 fb[py*TG_W+px] = col
317 px = px + 1
318 }
319 py = py + 1
320 }
321 return 0
322}
323func tg_w() -> i64 { return TG_W }
324func tg_h() -> i64 { return TG_H }