Compare commits

...

3 Commits

Author SHA1 Message Date
Lephenixnoir 39310e7e2e
basic level loading 2023-04-23 10:26:23 +02:00
Lephenixnoir cca75b89a6
incremental optimizations and improvements 2023-04-23 09:35:34 +02:00
Lephenixnoir 8c952c9e66
roll and basic damage plane 2023-04-23 00:17:11 +02:00
6 changed files with 276 additions and 99 deletions

View File

@ -21,7 +21,6 @@ endif()
if(AZUR_PLATFORM STREQUAL linux)
list(APPEND SOURCES
src/backend/linux/render.cc
src/backend/linux/programs.cc)
endif()
@ -34,7 +33,8 @@ if(AZUR_PLATFORM STREQUAL emscripten)
endif()
add_executable(chaos-drop ${SOURCES} ${ASSETS})
target_compile_options(chaos-drop PRIVATE -Wall -Wextra -O2)
target_compile_options(chaos-drop PRIVATE -Wall -Wextra -O3
-fmodulo-sched -fmodulo-sched-allow-regmoves)
if(AZUR_PLATFORM STREQUAL gint)
find_package(Gint 2.10 REQUIRED)

View File

@ -9,8 +9,9 @@ struct cd_raytrace_cmd
uint8_t shader_id;
uint8_t _[3];
/* Camera used for rendering */
/* Camera and world used for rendering */
struct camera const *camera;
struct world const *world;
/* Current y value */
int y;
};
@ -26,7 +27,7 @@ void cd_raytrace_shader(void *uniforms0, void *cmd0, void *fragment)
int frag_height = uniforms;
int h = (frag_height > 112 - cmd->y) ? 112 - cmd->y : frag_height;
render_fragment(cmd->camera, (uint16_t *)fragment, cmd->y, h);
render_fragment(cmd->camera, cmd->world, (uint16_t *)fragment, cmd->y, h);
cmd->y += h;
}
@ -36,13 +37,14 @@ static void register_shader(void)
CD_RAYTRACE_SHADER_ID = azrp_register_shader(cd_raytrace_shader);
}
void cd_raytrace(struct camera const *camera)
void cd_raytrace(struct camera const *camera, struct world const *world)
{
prof_enter(azrp_perf_cmdgen);
struct cd_raytrace_cmd cmd;
cmd.shader_id = CD_RAYTRACE_SHADER_ID;
cmd.camera = camera;
cmd.world = world;
cmd.y = 0;
azrp_queue_command(&cmd, sizeof cmd, 0, azrp_frag_count);

View File

@ -46,20 +46,63 @@ struct camera {
/* Corresponding directions, in world coordinates */
vec3 forward, right, up;
/* Game stuff... */
int roll_quadrant;
};
num neon_position;
struct element_mirror {
num begin, end;
};
enum element_plane_type {
ELEMENT_PLANE_DAMAGE,
ELEMENT_PLANE_ARROW,
};
struct element_plane {
num y;
uint32_t shape;
uint16_t type;
uint16_t data; /* ARROW: direction */
};
struct element_text {
num begin, end;
char const *str;
};
struct level {
int mirror_count;
struct element_mirror *mirrors;
int plane_count;
struct element_plane *planes;
int text_count;
struct element_text *texts;
};
struct world {
num16 neon_position;
num16 neon_period;
/* Current objects */
num depth;
struct element_mirror *mirror;
struct element_plane *plane;
struct element_text *text;
int mirror_index;
int plane_index;
int text_index;
};
void camera_set_fov(struct camera *camera, float fov_degrees);
mat3 matrix_camera2world(struct camera const *camera);
void camera_update_angles(struct camera *camera);
void render_fragment(struct camera const *camera, uint16_t *fragment,
int y_start, int y_height);
void render_fragment(struct camera const *camera, struct world const *world,
uint16_t *fragment, int y_start, int y_height);
bool camera_rolling(struct camera const *camera);
void camera_roll(struct camera *camera, float snap_factor);
//=== Azur shader wrapping the raytracing ===//
void cd_raytrace(struct camera const *camera);
void cd_raytrace(struct camera const *camera, struct world const *world);
void cd_raytrace_configure(void);
//=== Input management ===//
@ -70,6 +113,7 @@ struct input {
bool up;
bool down;
bool OPTN;
bool roll;
};
//=== Additions to libnum ===//

View File

@ -72,6 +72,7 @@ static int platform_update(struct input *input)
input->down = state[SDL_SCANCODE_DOWN];
input->left = state[SDL_SCANCODE_LEFT];
input->right = state[SDL_SCANCODE_RIGHT];
input->roll = state[SDL_SCANCODE_RETURN];
return 0;
}
@ -113,6 +114,8 @@ static int platform_update(struct input *input)
gint_osmenu();
if(e.key == KEY_OPTN)
input->OPTN = true;
if(e.key == KEY_SHIFT)
input->roll = true;
}
input->left = keydown(KEY_LEFT);
@ -172,13 +175,15 @@ vec3 operator * (mat3 const &M, vec3 const &u)
}
static struct camera *camera = NULL;
static struct world *world = NULL;
static struct level *level = NULL;
static bool debug = false;
void render(void)
{
#ifdef AZUR_TOOLKIT_GINT
azrp_perf_clear();
cd_raytrace(camera);
cd_raytrace(camera, world);
platform_render();
if(debug) {
@ -189,7 +194,7 @@ void render(void)
r61524_display(gint_vram, DHEIGHT-20, 20, R61524_DMA_WAIT);
}
#else
render_fragment(camera, vram, 0, VHEIGHT);
render_fragment(camera, world, vram, 0, VHEIGHT);
platform_render();
#endif
}
@ -199,58 +204,107 @@ int update(void)
struct input input = {};
if(platform_update(&input))
return 1;
if(input.OPTN)
debug = !debug;
/* Yes I'm aware I'm always changing the angles */
bool changed = false;
static num const mv_speed = 0.35;
static num const mv_speed = 0.45;
static num const wall = WORLD_SIZE * 0.45; /* margin */
static float const rot_speed = 0.01;
static float const rot_snap = 0.6;
static float const rot_max = 0.2;
static num const neon_speed = 2;
static num const fall_speed = 2;
static float const barrel_snap = 0.5;
if(input.left) {
camera->pos.x = max(camera->pos.x - mv_speed, -wall);
camera->pos -= mv_speed * camera->right;
camera->yaw = fmaxf(camera->yaw - rot_speed, -rot_max);
changed = true;
}
else if(input.right) {
camera->pos.x = min(camera->pos.x + mv_speed, wall);
camera->pos += mv_speed * camera->right;
camera->yaw = fminf(camera->yaw + rot_speed, rot_max);
changed = true;
}
else {
camera->yaw *= rot_snap;
changed = true;
}
if(input.up) {
camera->pos.z = min(camera->pos.z + mv_speed, wall);
camera->pos += mv_speed * camera->up;
camera->pitch = fmaxf(camera->pitch - rot_speed, -rot_max);
changed = true;
}
else if(input.down) {
camera->pos.z = max(camera->pos.z - mv_speed, -wall);
camera->pos -= mv_speed * camera->up;
camera->pitch = fminf(camera->pitch + rot_speed, rot_max);
changed = true;
}
else {
camera->pitch *= rot_snap;
changed = true;
}
if(changed)
camera_update_angles(camera);
camera->pos.x = clamp(camera->pos.x, -wall, wall);
camera->pos.z = clamp(camera->pos.z, -wall, wall);
camera->neon_position =
(camera->neon_position + num(32) - neon_speed) % num(32);
if(input.roll && input.left && !camera_rolling(camera)) {
camera->roll_quadrant = (camera->roll_quadrant + 4 - 1) % 4;
}
else if(input.roll && input.right && !camera_rolling(camera)) {
camera->roll_quadrant = (camera->roll_quadrant + 1) % 4;
}
camera_roll(camera, barrel_snap);
camera_update_angles(camera);
world->depth += fall_speed;
world->neon_position += world->neon_period - num16(fall_speed);
world->neon_position %= world->neon_period;
// Remove old world elements
struct world *w = world;
if(w->mirror && w->depth > w->mirror->end)
w->mirror = NULL;
if(w->plane && w->depth > w->plane->y) {
// TODO: Check collision
w->plane = NULL;
}
if(w->text && w->depth > w->text->end)
w->text = NULL;
// Load incoming world elements
if(!w->mirror && w->mirror_index < level->mirror_count
&& level->mirrors[w->mirror_index].begin < w->depth + num(64)) {
w->mirror = &level->mirrors[w->mirror_index++];
}
if(!w->plane && w->plane_index < level->plane_count
&& level->planes[w->plane_index].y < w->depth + num(64)) {
w->plane = &level->planes[w->plane_index++];
}
if(!w->text && w->text_index < level->text_count
&& level->texts[w->text_index].begin < w->depth + num(64)) {
w->text = &level->texts[w->text_index++];
}
if(input.OPTN)
debug = !debug;
return 0;
}
/* Basic basic level */
struct level lv1 = {
.mirror_count = 1,
.mirrors = (struct element_mirror[]){
{ .begin = 256, .end = 512 },
},
.plane_count = 2,
.planes = (struct element_plane[]){
{ .y = 64, .shape = 0b01010'10101'01010'10101'01010,
.type = ELEMENT_PLANE_DAMAGE, .data = 0 },
{ .y = 128, .shape = 0b11111'10001'10001'10001'11111,
.type = ELEMENT_PLANE_DAMAGE, .data = 0 },
},
.text_count = 0,
.texts = (struct element_text[]){
},
};
int main(void)
{
if(azur_init("Chaos Drop!", 3*VWIDTH, 3*VHEIGHT))
@ -262,6 +316,17 @@ int main(void)
camera_set_fov(camera, 80.0);
camera_update_angles(camera);
level = &lv1;
struct world w = {};
w.neon_position = 0;
w.depth = 0;
w.neon_period = 64;
w.mirror = NULL;
w.plane = NULL;
w.text = NULL;
world = &w;
init();
int rc = azur_main_loop(render, 30, update, -1, AZUR_MAIN_LOOP_TIED);
quit();

View File

@ -3,6 +3,9 @@
#include <stdio.h>
#include <math.h>
/* 5-bit channels to RGB565 */
#define RGB(R, G, B) ((R << 11) | (G << 6) | (B))
void camera_set_fov(struct camera *camera, float fov_degrees)
{
camera->fov = num(fov_degrees);
@ -15,6 +18,45 @@ void camera_set_fov(struct camera *camera, float fov_degrees)
camera->screen_distance = num(sd);
}
bool camera_rolling(struct camera const *camera)
{
float target_roll = camera->roll_quadrant * M_PI / 2;
float distance = fabsf(target_roll - camera->roll);
/* Wrap-around case */
if(camera->roll > 6.0 && camera->roll_quadrant == 0)
distance = fabsf(target_roll - (camera->roll - 2*M_PI));
return distance > 0.2;
}
void camera_roll(struct camera *camera, float snap_factor)
{
float target_roll = camera->roll_quadrant * M_PI / 2;
float distance_nowrap = target_roll - camera->roll;
float distance_wrap_up = (target_roll + 2*M_PI) - camera->roll;
float distance_wrap_down = (target_roll - 2*M_PI) - camera->roll;
/* Snap with closest direction */
if(fabsf(distance_nowrap) < fabsf(distance_wrap_up)) {
if(fabsf(distance_nowrap) < fabsf(distance_wrap_down))
camera->roll += distance_nowrap * snap_factor;
else
camera->roll += distance_wrap_down * snap_factor;
}
else {
if(fabsf(distance_wrap_down) < fabsf(distance_wrap_down))
camera->roll += distance_wrap_down * snap_factor;
else
camera->roll += distance_wrap_up * snap_factor;
}
if(camera->roll < 0)
camera->roll += 2 * M_PI;
else if(camera->roll > 2 * M_PI)
camera->roll -= 2 * M_PI;
}
mat3 matrix_camera2world(struct camera const *camera)
{
num cos_r = camera->cos_r;
@ -93,9 +135,12 @@ svec3 svec3_of_vec3(vec3 v)
enum object {
/* Wall sides */
LEFT = 1, TOP, RIGHT, BOTTOM,
/* Object plane */
OBJECT_PLANE,
};
static int cast_ray(svec3 const &origin, svec3 const &rayDir, snum *t)
static int cast_ray(struct world const *world, svec3 const &origin,
svec3 const &rayDir, snum *t, num *collision_y)
{
snum tx_rx_rz, tz_rx_rz;
int hitx = 0, hitz = 0;
@ -104,7 +149,7 @@ static int cast_ray(svec3 const &origin, svec3 const &rayDir, snum *t)
tx_rx_rz = (snum(WORLD_SIZE / 2) - origin.x) * rayDir.z;
hitx = RIGHT;
}
else if(rayDir.x < 0) {
else if(__builtin_expect(rayDir.x < 0, 1)) {
tx_rx_rz = (snum(-WORLD_SIZE / 2) - origin.x) * rayDir.z;
hitx = LEFT;
}
@ -113,47 +158,63 @@ static int cast_ray(svec3 const &origin, svec3 const &rayDir, snum *t)
tz_rx_rz = (snum(WORLD_SIZE / 2) - origin.z) * rayDir.x;
hitz = TOP;
}
else if(rayDir.z < 0) {
else if(__builtin_expect(rayDir.z < 0, 1)) {
tz_rx_rz = (snum(-WORLD_SIZE / 2) - origin.z) * rayDir.x;
hitz = BOTTOM;
}
// static int done = 0;
// if(++done <= 4)
// printf("tx=%f tz=%f rx=%f rz=%f tx_rx_rz=%f tz_rx_rz=%f\n",
// (float)tx, (float)tz, (float)rayDir.x, (float)rayDir.z,
// (float)tx_rx_rz, (float)tz_rx_rz);
int rx_rz_sign = (rayDir.x < 0) ^ (rayDir.z < 0);
int hit = 0;
if(hitz && (!hitx || (tz_rx_rz < tx_rx_rz) ^ rx_rz_sign)) {
if(t) {
if(hitz == TOP)
*t = num16::div_positive(snum(WORLD_SIZE / 2) - origin.z,
rayDir.z);
else
*t = num16::div_positive(origin.z + snum(WORLD_SIZE / 2),
-rayDir.z);
}
return hitz;
if(hitz == TOP)
*t = num16::div_positive(snum(WORLD_SIZE / 2) - origin.z,
rayDir.z);
else
*t = num16::div_positive(origin.z + snum(WORLD_SIZE / 2),
-rayDir.z);
hit = hitz;
}
else if(hitx) {
if(t) {
if(hitx == RIGHT)
*t = num16::div_positive(snum(WORLD_SIZE / 2) - origin.x,
rayDir.x);
else
*t = num16::div_positive(origin.x + snum(WORLD_SIZE / 2),
-rayDir.x);
else if(__builtin_expect(hitx, 1)) {
if(hitx == RIGHT)
*t = num16::div_positive(snum(WORLD_SIZE / 2) - origin.x,
rayDir.x);
else
*t = num16::div_positive(origin.x + snum(WORLD_SIZE / 2),
-rayDir.x);
hit = hitx;
}
else return 0;
/* Compute intersection with object plane */
*collision_y = num(origin.y) + num(*t) * num(rayDir.y);
if(__builtin_expect(world->plane &&
(*collision_y > world->plane->y - world->depth), 0)) {
snum plane_y = snum(world->plane->y - world->depth);
snum plane_t = (plane_y - origin.y) / rayDir.y;
snum plane_x = origin.x + plane_t * rayDir.x;
snum plane_z = origin.z + plane_t * rayDir.z;
plane_x = (plane_x + snum(WORLD_SIZE / 2)) * snum(5.0 / WORLD_SIZE);
plane_z = (plane_z + snum(WORLD_SIZE / 2)) * snum(5.0 / WORLD_SIZE);
int cell_x = plane_x.ifloor();
int cell_z = plane_z.ifloor();
if((unsigned)cell_x < 5 && (unsigned)cell_z < 5) {
if((world->plane->shape >> (5*cell_z + cell_x)) & 1) {
*t = plane_t;
*collision_y = world->plane->y - world->depth;
return OBJECT_PLANE;
}
}
return hitx;
}
return 0;
return hit;
}
void render_fragment(struct camera const *camera, uint16_t *fragment,
int y_start, int y_height)
void render_fragment(struct camera const *camera, struct world const *world,
uint16_t *fragment, int y_start, int y_height)
{
svec3 origin = svec3_of_vec3(camera->pos);
svec3 forward = svec3_of_vec3(camera->forward);
@ -170,74 +231,79 @@ void render_fragment(struct camera const *camera, uint16_t *fragment,
svec3 rayDir_row = screen_center - origin
+ snum(VHEIGHT/2 - y_start) * pixel_dy
+ snum(0 - VWIDTH/2) * pixel_dx;
/* printf("%f %f %f\n",
(float)rayDir_row.x,
(float)rayDir_row.y,
(float)rayDir_row.z); */
for(int y = y_start; y < y_start + y_height; y++) {
svec3 rayDir = rayDir_row;
for(int x = 0; x < VWIDTH; x++) {
snum t;
int obj = cast_ray(origin, rayDir, &t);
int recolor = 0;
snum collision_y;
num coll_y;
int obj = cast_ray(world, origin, rayDir, &t, &coll_y);
bool darken = false;
if(obj == LEFT || obj == RIGHT) {
if(__builtin_expect(
world->mirror && (obj == LEFT || obj == RIGHT), 0)) {
svec3 collision;
collision.z = origin.z + t * rayDir.z;
if(collision.z >= snum(-WORLD_SIZE / 4)
&& collision.z < snum(WORLD_SIZE / 4)) {
/* Mirror covers only part of width of wall */
bool ok_z = collision.z >= snum(-WORLD_SIZE / 4)
&& collision.z < snum(WORLD_SIZE / 4);
/* Mirror also does not go on forever */
bool ok_y = world->mirror
&& coll_y >= (world->mirror->begin - world->depth)
&& coll_y < (world->mirror->end - world->depth);
if(ok_z && ok_y) {
collision.x = origin.x + t * rayDir.x;
collision.y = origin.y + t * rayDir.y;
collision.y = snum(coll_y);
rayDir.x = -rayDir.x;
obj = cast_ray(collision, rayDir, &t);
recolor = 1 + (obj == RIGHT);
collision_y = collision.y + t * rayDir.y;
obj = cast_ray(world, collision, rayDir, &t, &coll_y);
darken = true;
rayDir.x = -rayDir.x;
}
else {
collision_y = origin.y + t * rayDir.y;
}
}
else {
collision_y = origin.y + t * rayDir.y;
}
static uint16_t const colors[5] = {
0x0000, 0xf800, 0x07e0, 0x001f, 0xffe0,
static uint16_t const colors[6] = {
RGB(3, 4, 8),
RGB(3, 4, 8), /* Left */
RGB(5, 6, 9), /* Top */
RGB(7, 8, 11), /* Right */
RGB(5, 6, 9), /* Bottom */
RGB(31, 0, 0), /* Object plane */
};
uint16_t color = colors[obj];
/* Don't show neons that are too far to avoid flickering */
if(collision_y < 64) {
snum neon_pos = snum(camera->neon_position);
if(coll_y < 64 && obj != OBJECT_PLANE) {
snum neon_pos = world->neon_position;
if(obj == TOP || obj == BOTTOM)
neon_pos += snum(16);
snum neon = collision_y - neon_pos;
neon.v = neon.v & (snum(32).v - 1);
neon_pos += world->neon_period * snum(0.5);
snum neon = snum(coll_y) - neon_pos;
neon.v = neon.v & (world->neon_period.v - 1);
/* Also make neons larger when they're far to further avoid
flickering */
snum neon_size = 1;
if(collision_y > 20)
neon_size += ((collision_y-snum(20)) * snum(1.0/4));
if(coll_y > 20)
neon_size += ((snum(coll_y)-snum(20)) * snum(1.0/4));
if(neon <= neon_size)
color = 0xffff;
}
else if(__builtin_expect(coll_y >= 64, 0)) {
if(coll_y > 128)
color = (color & 0xc718) >> 3;
else if(coll_y > 96)
color = (color & 0xe79c) >> 2;
else
color = (color & 0xf7de) >> 1;
}
if(recolor == 1)
color = ((color & 0xf7de) >> 1) + (0xf000 >> 1);
if(recolor == 2)
color = ((color & 0xf7de) >> 1) + (0x001f >> 1);
if(darken)
color = (color & 0xf7de) >> 1;
*fragment++ = color;
rayDir += pixel_dx;
}
rayDir_row -= pixel_dy;
}
// printf("%f %f %f\n", (float)rayDir.x, (float)rayDir.y, (float)rayDir.z);
}