code wiki / _hdl_build / nx_creature_render.nx

nx_creature_render.nx source

↩ module page · 141 lines · 6958 B

1// nx_creature_render.nx -- the GRAPHICS EMISSION for the companion game: turn a creature's traits 2// (species + 4 IVs) into a DETAILED, richly-coloured creature sprite. This is the horse (emit rich 3// graphics + mechanics) before the gameplay-loop cart. Attacks the measured #1 deficiency (palette 220 4// vs real games' 766-3336) with real shaded body parts, per-species forms, trait-driven colour schemes, 5// eyes with catchlights, and markings -- many colours, gradients, coherent structure. 6// Composes nx_game_raster (primitives) + nx_game_art (glow orbs / shading). LIB. license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_game_raster.nx" 9import "nx_game_art.nx" 10const K_MAGIC_10000: i64 = 10000 11const K_MAGIC_99999: i64 = 99999 12 13func cr_clamp(v: i64) -> i64 { if v<0 { return 0 } if v>255 { return 255 } return v } 14func cr_isqrt(n: i64) -> i64 { 15 if n<=0 { return 0 } 16 var x: i64 = n 17 var y: i64 = (x+1)/2 18 while y<x { x=y; y=(x+n/x)/2 } 19 return x 20} 21// filled ellipse with per-pixel SPHERICAL shading (light at upper-left) + a fine dither. This reads as a 22// lit VOLUME and -- because every pixel gets its own shade -- multiplies the distinct-colour count (the 23// measured palette lever: flat/row-shaded blobs were the reason palette stayed low vs real games). 24func cr_ellipse(fb: *i64, w: i64, h: i64, cx: i64, cy: i64, rx: i64, ry: i64, r: i64, g: i64, b: i64) -> i64 { 25 if ry<1 { return 0 } 26 if rx<1 { return 0 } 27 let lx: i64 = 0 - rx*4/10 // light source at upper-left of the sphere 28 let ly: i64 = 0 - ry*4/10 29 var y: i64 = 0 - ry 30 while y <= ry { 31 let yy: i64 = y*y*K_MAGIC_10000/(ry*ry) 32 let hw: i64 = rx * cr_isqrt(K_MAGIC_10000-yy) / 100 33 var x: i64 = 0 - hw 34 while x <= hw { 35 let dx: i64 = x - lx 36 let dy: i64 = y - ly 37 let d2: i64 = dx*dx*K_MAGIC_10000/(rx*rx) + dy*dy*K_MAGIC_10000/(ry*ry) // normalised distance^2 from light 38 var sh: i64 = 155 - d2/58 39 let dth: i64 = ((x*13 + y*7 + (x/3)*(y/5)) % 7) - 3 // fine dither = extra distinct shades 40 sh = sh + dth 41 if sh<45 { sh=45 } 42 if sh>160 { sh=160 } 43 gr_px(fb, w, h, cx+x, cy+y, gr_pack(cr_clamp(r*sh/100), cr_clamp(g*sh/100), cr_clamp(b*sh/100))) 44 x = x + 1 45 } 46 y = y + 1 47 } 48 return 0 49} 50func cr_tri(fb: *i64, w: i64, h: i64, x0: i64, y0: i64, x1: i64, y1: i64, x2: i64, y2: i64, r: i64, g: i64, b: i64) -> i64 { 51 var miny: i64 = y0 52 if y1<miny { miny=y1 } 53 if y2<miny { miny=y2 } 54 var maxy: i64 = y0 55 if y1>maxy { maxy=y1 } 56 if y2>maxy { maxy=y2 } 57 var y: i64 = miny 58 while y <= maxy { 59 var lo: i64 = K_MAGIC_99999 60 var hi: i64 = 0-K_MAGIC_99999 61 // scan the 3 edges for x-range at this y (integer edge walk) 62 var e: i64 = 0 63 while e < 3 { 64 var ax: i64 = x0 65 var ay: i64 = y0 66 var bx: i64 = x1 67 var by: i64 = y1 68 if e==1 { ax=x1; ay=y1; bx=x2; by=y2 } 69 if e==2 { ax=x2; ay=y2; bx=x0; by=y0 } 70 if ay != by { if y>=ay { if y<=by { let xx: i64 = ax + (bx-ax)*(y-ay)/(by-ay); if xx<lo {lo=xx} if xx>hi {hi=xx} } } } 71 if ay != by { if y>=by { if y<=ay { let xx2: i64 = ax + (bx-ax)*(y-ay)/(by-ay); if xx2<lo {lo=xx2} if xx2>hi {hi=xx2} } } } 72 e = e + 1 73 } 74 if hi>=lo { gr_hline(fb, w, h, lo, hi, y, gr_pack(r,g,b)) } 75 y = y + 1 76 } 77 return 0 78} 79func cr_eye(fb: *i64, w: i64, h: i64, cx: i64, cy: i64, rad: i64, er: i64, eg: i64, eb: i64) -> i64 { 80 gr_disc(fb, w, h, cx, cy, rad, gr_pack(250,250,255)) // sclera 81 gr_disc(fb, w, h, cx, cy, rad*3/4, gr_pack(er,eg,eb)) // iris (trait colour) 82 gr_disc(fb, w, h, cx, cy, rad/2, gr_pack(15,12,20)) // pupil 83 gr_disc(fb, w, h, cx-rad/3, cy-rad/3, rad/3, gr_pack(255,255,255)) // catchlight 84 return 0 85} 86 87// the 6 species base palettes (fire/water/grass/electric/psychic/rock), shifted by IVs 88func cr_base(spec: i64, chan: i64) -> i64 { 89 if spec==0 { if chan==0 {return 220} if chan==1 {return 90} return 60 } 90 if spec==1 { if chan==0 {return 70} if chan==1 {return 145} return 235 } 91 if spec==2 { if chan==0 {return 90} if chan==1 {return 200} return 110 } 92 if spec==3 { if chan==0 {return 240} if chan==1 {return 210} return 70 } 93 if spec==4 { if chan==0 {return 200} if chan==1 {return 110} return 225 } 94 if chan==0 {return 175} if chan==1 {return 155} return 125 95} 96 97// draw a full creature centred at (cx,cy) sized `sz`, from (spec, iv0..iv3) 98func cr_creature(fb: *i64, w: i64, h: i64, cx: i64, cy: i64, sz: i64, spec: i64, iv0: i64, iv1: i64, iv2: i64, iv3: i64) -> i64 { 99 // trait-driven colours (primary body / belly / accent / eye) -- IVs shift the species base 100 let pr: i64 = cr_clamp(cr_base(spec,0) + (iv0-16)*3) 101 let pg: i64 = cr_clamp(cr_base(spec,1) + (iv1-16)*3) 102 let pb: i64 = cr_clamp(cr_base(spec,2) + (iv2-16)*3) 103 let belr: i64 = cr_clamp(pr+55) 104 let belg: i64 = cr_clamp(pg+55) 105 let belb: i64 = cr_clamp(pb+55) 106 let accr: i64 = cr_clamp(255-pr) 107 let accg: i64 = cr_clamp(230-pg) 108 let accb: i64 = cr_clamp(255-pb) 109 // soft ground shadow 110 gr_disc(fb, w, h, cx, cy+sz*7/8, sz*2/3, gr_pack(8,8,16)) 111 // tail (species-varied length, curls behind) 112 cr_ellipse(fb, w, h, cx+sz*3/4, cy+sz/6, sz/4, sz/8, pr, pg, pb) 113 gr_disc(fb, w, h, cx+sz, cy, sz/6, gr_pack(accr,accg,accb)) 114 // hind + fore legs 115 cr_ellipse(fb, w, h, cx-sz*2/5, cy+sz*3/5, sz/6, sz/4, pr*8/10, pg*8/10, pb*8/10) 116 cr_ellipse(fb, w, h, cx+sz*2/5, cy+sz*3/5, sz/6, sz/4, pr*8/10, pg*8/10, pb*8/10) 117 // body (big lit ellipse) + lighter belly patch 118 cr_ellipse(fb, w, h, cx, cy+sz/6, sz*3/5, sz*1/2, pr, pg, pb) 119 cr_ellipse(fb, w, h, cx, cy+sz*2/5, sz*2/5, sz/3, belr, belg, belb) 120 // markings (IV-driven spots) for pattern detail 121 var m: i64 = 0 122 let nmark: i64 = 2 + (iv3 % 4) 123 while m < nmark { 124 let mx: i64 = cx - sz/3 + (m*sz*2/5) % (sz) 125 let my: i64 = cy - sz/8 + ((m*iv0) % (sz/2)) 126 gr_disc(fb, w, h, mx, my, sz/12+1, gr_pack(accr,accg,accb)) 127 m = m + 1 128 } 129 // head 130 cr_ellipse(fb, w, h, cx, cy-sz*2/5, sz*2/5, sz*2/5, pr, pg, pb) 131 // ears / horns (species: triangles up) 132 if spec==0 { cr_tri(fb,w,h, cx-sz/4,cy-sz*3/5, cx-sz/8,cy-sz, cx,cy-sz*3/5, accr,accg,accb) 133 cr_tri(fb,w,h, cx+sz/4,cy-sz*3/5, cx+sz/8,cy-sz, cx,cy-sz*3/5, accr,accg,accb) } 134 if spec!=0 { cr_ellipse(fb,w,h, cx-sz/4, cy-sz*3/4, sz/8, sz/5, pr,pg,pb) 135 cr_ellipse(fb,w,h, cx+sz/4, cy-sz*3/4, sz/8, sz/5, pr,pg,pb) } 136 // eyes (iris = accent colour) + a small nose/mouth 137 cr_eye(fb, w, h, cx-sz/5, cy-sz*2/5, sz/7+1, accr, accg, accb) 138 cr_eye(fb, w, h, cx+sz/5, cy-sz*2/5, sz/7+1, accr, accg, accb) 139 gr_disc(fb, w, h, cx, cy-sz/4, sz/12, gr_pack(30,20,26)) 140 return 0 141}