-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathtr.cpp
523 lines (424 loc) · 15.7 KB
/
tr.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
/*
"SPHERY VS. SHAPES" by Victor Suarez Rovere and Manuel Suarez
Copyright (C) 2021 Victor Suarez Rovere <[email protected]>
HOW TO PLAY:
Press the left mouse button or the UP KEY to jump.
Score increases when you're on floor, you win when the bar is full.
*/
//#define GOD_MODE
//#define NON_INTERACTIVE
//#define SOFT_SHADOW 1 //2 for smoother border transition
//#define LEVELS
//#define ALTERNATE_UI 3 //level of graphics detail
//ALTERNATE_UI=1 640x480: synths in Arty35T @35.60 MHz, 2199/33280 cells, 0 latency)
//ALTERNATE_UI=2 640x480: synths in Arty35T @27.43 MHz, 3479/33280 cells, 0 latency)
//ALTERNATE_UI=3 640x480: synths in Arty35T @25.74 MHz, 7651/33280 cells, 5 latency)
//#define RT_SMALL_UI //enable to reduce raytracing complexity (without RT, 31619(comb only) / 20800 max, with RT ~23702 board=???)
//RT_SMALL_UI 640x480: synths in Arty35T @27.63 MHz, about 27000/33280 cells, 28 latency)
//#define DITHER
//#define ANTIALIAS 6 //default 6, smooth 4
#define SCREEN_ASPECT 16./9. //or for example 10./9.
#include "tr.h"
typedef coord_type hole_t; //TODO: move
#define get_scene() state.scene
#define BLINKY
#define SCORE_STEP 1
full_state_t state;
hole_t plane_has_hole(hole_t x, hole_t z)
{
hole_t ret = 1.;
x=fixed_shr(x, 4);
z=fixed_shr(z, 5);
int16_t ix = round16(x);
int16_t iz = round16(z);
hole_t fracx = hole_t(ix)-x;
hole_t fracz = hole_t(iz)-z;
uint16_t hx = hash16(ix) >> 5;
uint16_t hz = hash16(iz) >> 5;
if((hx ^ hz) < int16_t(ix+600))
{
hole_t ax = fixed_abs(fracx);
hole_t az = fixed_abs(fracz);
ret = (ax + az) - hole_t(RHOMBUS_SIZE);
}
return ret;
}
inline scene_colors_t scene_colors(IN(scene_t) scene)
{
uint2_t channel = scene.current_color_channel;
scene_colors_t r;
if(channel == 0)
{
r.sphere.diffuse_color = scene.sphere.material.diffuse_color.r;
r.sphere.reflect_color = scene.sphere.material.reflect_color.r;
r.plane.diffuse_color = scene.plane.material.diffuse_color.r;
r.plane.reflect_color = scene.plane.material.reflect_color.r;
r.plane_color1 = scene.plane.color1.r;
r.plane_color2 = scene.plane.color2.r;
r.fog = scene.fog.r;
}
else if(channel == 1)
{
r.sphere.diffuse_color = scene.sphere.material.diffuse_color.g;
r.sphere.reflect_color = scene.sphere.material.reflect_color.g;
r.plane.diffuse_color = scene.plane.material.diffuse_color.g;
r.plane.reflect_color = scene.plane.material.reflect_color.g;
r.plane_color1 = scene.plane.color1.g;
r.plane_color2 = scene.plane.color2.g;
r.fog = scene.fog.g;
}
else
{
r.sphere.diffuse_color = scene.sphere.material.diffuse_color.b;
r.sphere.reflect_color = scene.sphere.material.reflect_color.b;
r.plane.diffuse_color = scene.plane.material.diffuse_color.b;
r.plane.reflect_color = scene.plane.material.reflect_color.b;
r.plane_color1 = scene.plane.color1.b;
r.plane_color2 = scene.plane.color2.b;
r.fog = scene.fog.b;
}
return r;
}
//raytracer math inspired on tinyraytracer https://github.com/ssloy/tinyraytracer
struct point_and_dir
{
vec3 orig, dir;
};
struct hit_out
{
float dist, borderdist;
point_and_dir hit;
};
//#warning: this seems to add cells to synth, FIXME: inline and define 'hit_out hitout;' at top
hit_out sphere_hit(bool hit, IN(vec3) center, IN(point_and_dir) hitin, float t, float diff)
{
hit_out hitout;
hitout.dist = hit ? t : RAY_NOINT;
//if(hit) //always calculated to save hardware muxes
{
hitout.hit.orig = hitin.orig + hitin.dir*hitout.dist;
hitout.hit.dir = normalize(hitout.hit.orig - center);
}
hitout.borderdist = diff;
return hitout;
}
hit_out ray_sphere_intersect(IN(vec3) center, IN(point_and_dir) hitin)
{
vec3 rc = hitin.orig - center;
float b = dot(rc, hitin.dir);
float c = dot(rc, rc) - SPHERE_RADIUS*SPHERE_RADIUS;
float diff = b*b - c;
bool nothit = is_negative(diff);
float t = RAY_NOINT;
if(!nothit)
{
t = -(b + sqrt(diff));
nothit = is_negative(t);
if (nothit)
diff = -diff;
}
return sphere_hit(!nothit, center, hitin, t, diff);
}
hit_out ray_plane_intersect(IN(plane_t) plane, IN(point_and_dir) hitin)
{
hit_out hitout;
hitout.dist = RAY_NOINT;
hitout.borderdist = 0.;
vec3 plane_center = object_coord_to_float3(plane.center);
float d;
vec3 pt;
hole_t hole_margin = 0; //FIXME: parser needs initialization
vec3 o;
if (hitin.dir.y != 0.) // avoids division by zero
//if (is_negative(hitin.dir.y)) // avoids division by zero
//if (float_abs(hitin.dir.y) > EPS) // avoid division by zero
{
//d = -(hitin.orig.y-plane_center.y)/hitin.dir.y;
d = float_fast_div_u(hitin.orig.y-plane_center.y, -hitin.dir.y);
pt = hitin.orig + hitin.dir*d;
if (d>EPS) //strict gt
{
o = pt - plane_center;
hole_margin = plane_has_hole(hole_t(o.x), hole_t(o.z));
if(!fixed_is_negative(hole_margin))
{
hitout.dist = d;
hitout.hit.orig = pt;
vec3 N = VECTOR_NURMAL_UPWARDS;
hitout.hit.dir = N; //points upwards
}
hitout.borderdist = float(hole_margin);
}
}
return hitout;
}
color_basic_t sphere_effect(IN(hit_out) hit, IN(render_material_t) hit_material)
{
color_basic_t rcolor = hit_material.diffuse_color;
IN(scene_t) scene = get_scene();
IN(scene_colors_t) colors = scene_colors(scene);
IN(sphere_t) s = scene.sphere;
uint16_t frame = scene.frame;
uint8_t tick = frame>>1;
if((tick & 0x3F) != 1 || ((hash16(tick)>>13) & 1) != 0)
{
//eyeballs
float dy = (hit.hit.dir.y-float_shift(float(s.center.y),-6)*1.5); //FIXME: optimize constants 1.5, 1.25
float dx = float_shift(float_abs(hit.hit.dir.z-hit.hit.dir.x)-.6, -1)*1.25;
float d = dx*dx+dy*dy;
coord_type mindist = fixed_shr(s.heat, 4) + .25*.25;
if(coord_type(d) < mindist)
rcolor = d < .15*.15 ? color_basic_t(0.) : color_basic_t(1.2);
}
return rcolor;
}
color_basic_t plane_effect(IN(hit_out) hit)
{
IN(scene_t) scene = get_scene();
IN(scene_colors_t) colors = scene_colors(scene);
IN(plane_t) plane = scene.plane;
color_basic_t rcolor = colors.plane.diffuse_color;
vec3 plane_center = object_coord_to_float3(plane.center);
float hitx = hit.hit.orig.x - plane_center.x;
float hitz = hit.hit.orig.z - plane_center.z;
float ox = float_shift(hitx, -FLOOR_SHIFT); //FIXME: same coordinates in this game
float oz = float_shift(hitz, -FLOOR_SHIFT);
int16_t ix = round16(ox);
int16_t iz = round16(oz);
static const color_type bk = .3;
color_basic_t color2 = colors.plane_color2;
rcolor = ((ix ^ iz) & 1) != 0 ? colors.plane_color1 : color2;
if(hit.borderdist < HOLE_BORDER)
{
rcolor = colors.plane.diffuse_color;
}
return rcolor;
}
color_basic_t background_color(float dir_y)
{
color_type y = is_negative(dir_y) ? color_type(0.) : color_type(dir_y*dir_y);
return color_basic_t(y);
}
color_basic_t light_intensity(IN(vec3) hit)
{
//light_intensity optimized for fixe points
coord_type lz = (coord_type(hit.z)-LIGHT_Z)*coord_type(1./LIGHT_Y);
coord_type lx = coord_type(hit.x)*coord_type(1./LIGHT_Y);
coord_type dl = lx*lx + 1. + lz*lz;
return color_basic_t(inversesqrt(float(dl))); //FIXME: implement RSQRT for fixed points
}
color_basic_t cast_ray_nested(IN(point_and_dir) hitin)
{
IN(scene_t) scene = get_scene();
IN(scene_colors_t) colors = scene_colors(scene);
render_material_t hit_material;
hit_material = colors.sphere;//this is what's reflected on the floor
hit_out hitout = ray_sphere_intersect(object_coord_to_float3(scene.sphere.center), hitin);
hit_out hitplane = ray_plane_intersect(scene.plane, hitin);
if (hitplane.dist < hitout.dist)
{
//this controls what's reflected on the sphere
hitout = hitplane;
hit_material = colors.plane;
hit_material.diffuse_color = plane_effect(hitout);
}
color_basic_t rcolor = color_basic_t(0.);
//#warning solve need to initialize
if (hitout.dist >= float_shift(1., DIST_SHIFT))
rcolor = background_color(hitin.dir.y); //has other direction
else
rcolor = hit_material.diffuse_color*light_intensity(hitout.hit.orig);
return rcolor;
}
color_basic_t shade(IN(color_basic_t) background, IN(vec3) dir, IN(hit_out) hit, IN(render_material_t) hit_material, color_type minfog)
{
IN(scene_t) scene = get_scene();
IN(scene_colors_t) colors = scene_colors(scene);
color_basic_t rcolor = background;
float fogmix = float_shift(hit.dist, -DIST_SHIFT); //no need to accumulated dist
if (fogmix < 1.)
{
point_and_dir hitreflect;
hitreflect.orig = hit.hit.orig;
hitreflect.dir = reflect(dir, hit.hit.dir);
color_basic_t reflect_color = cast_ray_nested(hitreflect);
color_basic_t li = light_intensity(hit.hit.orig);
color_basic_t diffuse_color = hit_material.diffuse_color * (li + AMBIENT_INTENSITY);
color_basic_t comb_color = diffuse_color + reflect_color*hit_material.reflect_color;
rcolor = color_select(color_max(color_type(fogmix), minfog), colors.fog, comb_color);
}
return rcolor;
}
bool is_star(float x, float y)
{
return ((hashf(x)>>2) & (hashf(y)>>2)) > 0x3E00;
}
color_basic_t cast_ray(IN(point_and_dir) hitin)
{
IN(scene_t) scene = get_scene();
IN(scene_colors_t) colors = scene_colors(scene);
float ys = float_abs(float_shift(hitin.dir.y, 1));
bool has_star = is_star(hitin.dir.x, hitin.dir.y);
color_basic_t sky = has_star ? color_basic_t(STAR_INTENSITY) : background_color(hitin.dir.y);
color_type mix = ys<1. ? color_type(1)-color_type(ys): color_type(0);
color_basic_t bfog = color_select(mix, colors.fog, sky);
hit_out hitsphere = ray_sphere_intersect(object_coord_to_float3(scene.sphere.center), hitin);
render_material_t sphere_material = colors.sphere; //FIXME: needed?
sphere_material.diffuse_color = sphere_effect(hitsphere, colors.sphere);
hit_out hitplane = ray_plane_intersect(scene.plane, hitin);
render_material_t planematerial;
planematerial = colors.plane; //FIXME: needed?
planematerial.diffuse_color = plane_effect(hitplane);
bool planehit = hitplane.dist < hitsphere.dist;
render_material_t hit_material;
hit_material = planehit ? planematerial : sphere_material;
hit_out hitout = planehit ? hitplane : hitsphere;
color_type c = planehit ? mix : color_type(0.);
color_basic_t rcolor = shade(bfog, hitin.dir, hitout, hit_material, c); //no fog for sphere
return rcolor;
}
color_basic_t render_pixel_internal(screen_coord_t x, screen_coord_t y)
{
IN(scene_t) scene = get_scene();
IN(scene_colors_t) colors = scene_colors(scene);
point_and_dir hitin;
hitin.orig = object_coord_to_float3(scene.camera);
vec3 camera_dir = {float(x), float(y), float(-1.)};
hitin.dir = normalize(camera_dir);
return cast_ray(hitin);
}
full_state_t reset_state(uint16_t score)
{
full_state_t state;
material_t gold;
gold.diffuse_color = K_gold_color;
gold.reflect_color = K_gold_reflect_color;
material_t floor_material;
floor_material.diffuse_color = K_floor_difusse;
floor_material.reflect_color = K_floor_reflect;
state.scene.plane.center = K_plane_center_start;
state.scene.plane.material = floor_material;
state.scene.plane.color1 = K_plane_color1;
state.scene.plane.color2 = K_plane_color2;
state.scene.sphere.center = K_sphere_center_start;
state.scene.sphere.material = gold;
state.scene.sphere.heat = 0.;
state.scene.camera = K_camera_pos_start;
state.scene.frame = 0;
state.scene.scorebar = 0;
state.scene.fog = K_fog_color;
state.plane_y = coord_type(state.scene.plane.center.y);
state.sphere_x = coord_type(state.scene.sphere.center.x);
state.sphere_z = coord_type(state.scene.sphere.center.z);
state.gold_color = gold.diffuse_color;
state.gold_reflect_color = gold.reflect_color;
state.lava_color = K_lava_color;
state.sphere_y = coord_type(state.scene.sphere.center.y);
state.heat = state.scene.sphere.heat;
state.camera_y = coord_type(state.scene.camera.y);
state.camera_z = coord_type(state.scene.camera.z);
state.plane_x = coord_type(FLOOR_X);
state.sphere_xvel = 0.;
state.sphere_yvel = 0.;
state.won = false;
state.score = score;
return state;
}
full_state_t full_update(INOUT(full_state_t) state, bool reset, bool button_state)
{
uint16_t score = state.score;
if(reset) score = 0;
state.plane_x = state.plane_x + state.sphere_xvel;
state.sphere_yvel = state.sphere_yvel + GRAVITY_CONSTANT;
state.sphere_y = state.sphere_y - state.sphere_yvel;
coord_type underground = (state.sphere_y - SPHERE_RADIUS) - FLOOR_Y; //PLANE_Y is always 0
if(state.won)
state.sphere_yvel = fixed_shr(underground, 4);
if(fixed_is_negative(underground))
{
state.sphere_xvel = state.sphere_xvel - XVEL_CONSTANT;
coord_type coord_x = state.sphere_x - state.plane_x;
coord_type coord_z = state.sphere_z - state.plane_x; //z=x
bool half_up = state.sphere_y > state.plane_y;
if(half_up && state.won == false)
{
//TODO: if the plane has a hole can be calculated at rendering time and reused!
if(plane_has_hole(coord_x, coord_z) > -HOLE_GUARD_MARGIN) // > about -.1 gives margin for the ball size
{
state.score = state.score+SCORE_STEP;
if(state.score >= MAXSCORE && state.won!=true)
state.won = true;
if(button_state)
{
state.sphere_yvel = -JUMP_CONSTANT;
state.sphere_xvel = -XVEL_DEFAULT;
}
else
state.sphere_yvel = -GRAVITY_CONSTANT_LIGHT;
}
}
else
{
state.camera_z = state.camera_z-fixed_shr(underground, ZOOMOUT_CONSTANT); //lose => fadeout
}
}
state.camera_y = state.camera_y + fixed_shr(state.sphere_y - state.camera_y, 5);
//write all outputs
state.lose = fixed_is_negative(underground) && ((-int16_t(underground) >> 10) != 0); //underground < -2048.
state.diffuse_color = state.gold_color;
state.reflect_color = state.gold_reflect_color;
state.scorebar = state.won ? 0 : state.score;
if(state.lose)
reset = true;
if(reset)
state = reset_state(score);
state.scene.sphere.center.y = state.sphere_y;
state.scene.sphere.heat = state.heat;
state.scene.camera.y = state.camera_y;
state.scene.camera.z = state.camera_z;
state.scene.plane.center.x = state.plane_x;
state.scene.plane.center.z = state.plane_x;
state.scene.sphere.material.diffuse_color = state.diffuse_color;
state.scene.sphere.material.reflect_color = state.reflect_color;
state.scene.sphere.yvel = state.sphere_yvel;
state.scene.scorebar = state.scorebar; //FIXME: move to state update function to make this function wires-only
state.scene.frame = state.scene.frame + 1;
return state;
}
inline pixel_t render_pixel(uint16_t i, uint16_t j
, pixel_t pix_in
)
{
IN(scene_t) scene = get_scene();
int16_t cx = i << 1;
cx = cx - (FRAME_WIDTH + 1);
int16_t cy = j << 1;
cy = (FRAME_HEIGHT + 1) - cy;
const float W = (float)FRAME_WIDTH;
const float H = (float)FRAME_HEIGHT;
static const screen_coord_t ax = 1024.*(SCREEN_ASPECT)/W;
static const screen_coord_t ay = 1024./H;
screen_coord_t x = fixed_shr(cx, 10+1) * ax;
screen_coord_t y = fixed_shr(cy, 10+1) * ay;
pixel_t pix; //ignores alpha
static const uint16_t score_factor = 2048*(FRAME_WIDTH-2*SCORE_MARGINS)/MAXSCORE;
uint16_t scorebar = score_factor*scene.scorebar >> 11;
if(i >= SCORE_MARGINS && i < SCORE_MARGINS + scorebar && j > SCORE_MARGINS && j < 2*SCORE_MARGINS)
{
pix.r = 0; pix.g = 200; pix.b = 0; // pix = color(0., 200./255., 0.);
}
else
{
pix = pix_in;
color_type c = render_pixel_internal(x, y);
uint16_t c9 = (uint16_t)fixed_asshort(c, 8);
uint8_t c8 = (uint8_t) ((c9 & ~0xFF) ? (uint8_t)0xFF:(uint8_t)c9);
if(scene.current_color_channel == 0)
pix.r = c8;
else if(scene.current_color_channel == 1)
pix.g = c8;
else
pix.b = c8;
}
return pix;
}