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}