code wiki / (root) / nx_treegen.nx

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 }