code wiki / _hdl_build / nx_creature_render.nx

nx_creature_render.nx source

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