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}