clean up all code but main.cc + SDL version

This commit is contained in:
Lephenixnoir 2023-04-23 23:31:24 +02:00
parent 2437e4abfc
commit 7c52c73716
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
7 changed files with 319 additions and 259 deletions

View File

@ -9,6 +9,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(SOURCES
src/main.cc
src/game.cc
src/raytracing.cc)
set(ASSETS)

View File

@ -16,12 +16,9 @@ struct cd_raytrace_cmd
int y;
};
#include <string.h>
/* TODO: Write raytrace shader in assembler (MUCH NEEDED) */
/* TODO: Write (part of) the raytrace shader in assembler */
void cd_raytrace_shader(void *uniforms0, void *cmd0, void *fragment)
{
memset(fragment, 0x55, azrp_width * azrp_frag_height * 2);
uint32_t uniforms = (uint32_t)uniforms0;
struct cd_raytrace_cmd *cmd = (struct cd_raytrace_cmd *)cmd0;

View File

@ -1,57 +1,91 @@
/* The world is a drop chute, arranged horizontally. The coordinate system is
right-handed, with the following orientation from the top view of the chute:
/* Chaos Drop: A classic falling game incorporating raytracing */
z
^
|
|
y (x)-----> x
#pragma once
The player primarily moves in the +y direction (... although in practice
objects move towards the player to better use the limited range of fixed-
point values).
We compute directions of rays based on points on the virtual screen placed
in the world, in front of the camera. We make rays start at the camera
position so the distance between the camera and screen does not matter; we
arbitrarily place the screen at a distance which makes the screen height
correspond to 2*HALF_SCREEN_HEIGHT world units. This is to help avoid
precision loss with fixed-point numbers. */
#define __BSD_VISIBLE 1
#include <math.h>
#include <num/num.h>
#include <num/vec.h>
using namespace libnum;
struct mat3;
#include "util.h"
/* Number of world units that we fix the screen's half height to. The screen is
placed at the correct distance to make that happen. */
//--
// World and camera
//---
/* World boundaries (on each side; the tube is thus 8x8). The size doesn't
matter for modeling purposes but this value is chosen to facilitate low-
precision computations with num16. */
#define WORLD_SIZE 4
/* Size, in world space, that half of the virtual screen's height should
occupy. Larger values will push the virtual screen away from the camera.
Mathematically any positive value would work. This value is again chosen to
improve the precision of num16 computations. */
#define HALF_SCREEN_HEIGHT 2
/* World boundaries */
#define WORLD_SIZE 8 /* (± 4) */
/* Viewport size, in pixels */
#define VWIDTH 198
#define VHEIGHT 112
/* Viewport/virtual screen size, in pixels */
#define VWIDTH 198
#define VHEIGHT 112
/* Coordinate system
The world is a drop chute, arranged horizontally to fit Azur's standard
coordinate system. Coordinates are right-handed, with the following
orientation looking forward (down the chute):
z
^
|
y (x)---> x
There is no player-centered coordinate system since we do all the raytracing
in the world space. */
struct camera {
/* Horizontal FOV in degrees */
num fov;
/* Screen distance (see raytracing.cc for details on how this is chosen) */
num screen_distance;
/* Camera position (usually stays at z=0, objects move towards it) */
/* Camera position in world space */
vec3 pos;
/* Angles of rotation */
float yaw, pitch, roll;
num cos_r, sin_r;
num cos_p, sin_p;
num cos_y, sin_y;
/* Corresponding directions, in world coordinates */
/* Corresponding directions, in world coordinates. This forms a local
coordinate system (which is only used ever for ray generation) */
vec3 forward, right, up;
/* (Game-specific) Targeted roll position (0..3) */
int roll_quadrant;
};
/* Set the camera's horizontal FOV in degrees. */
void camera_set_fov(struct camera *camera, float fov_degrees);
/* Compute the tranformation matrix that turns coordinates relative to the
camera into world coordinates. */
mat3 camera_camera2world_matrix(struct camera const *camera);
/* Update camera's internal variables after modifying the angles. */
void camera_update_angles(struct camera *camera);
/* Determines whether the camera is rolling. This function returns true only
during an initial portion of the roll animation. It returns false as soon as
the player should be allowed to roll again. */
bool camera_rolling(struct camera const *camera);
/* Perform rolling by bringing the roll angle closer to the targeted roll
position. How much closer is controlled by 0 < snap_factor 1. With
snap_factor=1, the roll snaps instantly. Any other constant value gives an
exponential snap. */
void camera_roll(struct camera *camera, float snap_factor);
//---
// Game levels
//---
/* Mirror along the left and right walls. */
struct element_mirror {
num begin, end;
};
/* Object plane intersecting the chute. */
enum element_plane_type {
ELEMENT_PLANE_DAMAGE,
ELEMENT_PLANE_ARROW,
@ -61,17 +95,25 @@ struct element_plane {
num y;
uint32_t shape;
uint16_t type;
uint16_t data; /* ARROW: direction */
/* ARROW: arrow direction (same numbering as camera->roll_quadrant) */
uint16_t data;
};
/* Tutorial text or sarcastic commentary. */
struct element_text {
num begin, end;
char const *str;
};
struct level {
/* Level name (usually just two digits) */
char const *name;
/* Depth at which the level is completed */
num finish;
/* Lists of elements, all of which should be sorted by increasing order of
begin/y members (and only one element of each type should exist for each
value of y; if there are multiple, only the first will be shown) */
int mirror_count;
struct element_mirror *mirrors;
@ -82,6 +124,14 @@ struct level {
struct element_text *texts;
};
/* Check whether world coordinates x/z would collide with an object plane of
the provided shape. */
bool level_plane_collides(num16 x, num16 z, uint32_t shape);
//---
// Dynamic world information
//---
struct world {
num16 neon_position;
num16 neon_period;
@ -96,19 +146,20 @@ struct world {
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);
//---
// Raytracing
//---
/* Core function. Renders an image of the provided world through the provided
camera. Renders lines [y_start ... y_start+y_height) on the specified VRAM
fragment. */
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);
bool plane_collides(num16 x, num16 z, uint32_t shape);
//=== Azur shader wrapping the raytracing ===//
/* Azur shader wrapping the raytracing function. */
void cd_raytrace(struct camera const *camera, struct world const *world);
/* Shader configuration function, must be called at initialization and after
any scale change. */
void cd_raytrace_configure(void);
//=== Input management ===//
@ -126,15 +177,3 @@ struct input {
bool NEXT_LEVEL;
bool enter;
};
//=== Additions to libnum ===//
struct mat3
{
num x11, x12, x13;
num x21, x22, x23;
num x31, x32, x33;
};
mat3 operator *(mat3 const &A, mat3 const &B);
vec3 operator *(mat3 const &M, vec3 const &u);

123
src/game.cc Normal file
View File

@ -0,0 +1,123 @@
/* Functions for game-specific mechanics. */
#include "chaos-drop.h"
void camera_set_fov(struct camera *camera, float fov_degrees)
{
camera->fov = num(fov_degrees);
float fov_radians = fov_degrees * 3.14159 / 180;
/* Use FOV as the horizontal viewing angle */
float sd = (VWIDTH * HALF_SCREEN_HEIGHT / VHEIGHT) / tanf(fov_radians / 2);
/* The screen is at such a distance that 16 units is half the height. We
don't care where it is placed as we'll always send rays from the camera
itself. This is to ensure good ranges for fixed point values */
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.3;
}
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;
float mv;
/* Snap with closest direction */
if(fabsf(distance_nowrap) < fabsf(distance_wrap_up)) {
if(fabsf(distance_nowrap) < fabsf(distance_wrap_down))
mv = distance_nowrap * snap_factor;
else
mv = distance_wrap_down * snap_factor;
}
else {
if(fabsf(distance_wrap_down) < fabsf(distance_wrap_down))
mv = distance_wrap_down * snap_factor;
else
mv = distance_wrap_up * snap_factor;
}
camera->roll += mv;
if(camera->roll < 0)
camera->roll += 2 * M_PI;
else if(camera->roll > 2 * M_PI)
camera->roll -= 2 * M_PI;
/* Also rotate the player along with it */
float cos_mv, sin_mv;
sincosf(-mv, &sin_mv, &cos_mv);
num c = cos_mv, s = sin_mv;
num nx = camera->pos.x * c + camera->pos.z * s;
num nz = -camera->pos.x * s + camera->pos.z * c;
camera->pos.x = nx;
camera->pos.z = nz;
}
mat3 camera_camera2world_matrix(struct camera const *camera)
{
num cos_r = camera->cos_r;
num sin_r = camera->sin_r;
num cos_p = camera->cos_p;
num sin_p = camera->sin_p;
num cos_y = camera->cos_y;
num sin_y = camera->sin_y;
mat3 m_anti_roll = {
cos_r, 0, -sin_r,
0, 1, 0,
+sin_r, 0, cos_r,
};
mat3 m_anti_pitch = {
1, 0, 0,
0, cos_p, -sin_p,
0, +sin_p, cos_p,
};
mat3 m_anti_yaw = {
cos_y, -sin_y, 0,
+sin_y, cos_y, 0,
0, 0, 1,
};
return m_anti_yaw * (m_anti_pitch * m_anti_roll);
}
void camera_update_angles(struct camera *camera)
{
float c, s;
sincosf(camera->roll, &s, &c);
camera->sin_r = s;
camera->cos_r = c;
sincosf(camera->pitch, &s, &c);
camera->sin_p = s;
camera->cos_p = c;
sincosf(camera->yaw, &s, &c);
camera->sin_y = s;
camera->cos_y = c;
mat3 c2w = camera_camera2world_matrix(camera);
camera->forward = c2w * vec3(0, 1, 0);
camera->right = c2w * vec3(1, 0, 0);
camera->up = c2w * vec3(0, 0, 1);
}
bool level_plane_collides(num16 x, num16 z, uint32_t shape)
{
int cx = ((x + num16(WORLD_SIZE)) * num16(5.0 / 2 / WORLD_SIZE)).ifloor();
int cz = ((z + num16(WORLD_SIZE)) * num16(5.0 / 2 / WORLD_SIZE)).ifloor();
return (unsigned)cx < 5 && (unsigned)cz < 5
&& ((shape >> (5*cz + 4-cx)) & 1);
}

View File

@ -470,7 +470,7 @@ int update(void)
}
static num const mv_speed = 0.45;
static num const wall = WORLD_SIZE * 0.45; /* margin */
static num const wall = WORLD_SIZE * 0.9; /* margin */
static float const rot_speed = 0.01;
static float const rot_snap = 0.6;
static float const rot_max = 0.2;
@ -533,8 +533,9 @@ int update(void)
/* Apply plane effect */
if((w->plane->type == ELEMENT_PLANE_DAMAGE ||
w->plane->type == ELEMENT_PLANE_INVIS)
&& plane_collides(num16(camera->pos.x), num16(camera->pos.z),
w->plane->shape)) {
&& level_plane_collides(num16(camera->pos.x), num16(camera->pos.z),
w->plane->shape))
{
world_reset(w);
reset_frames = 2;
}

View File

@ -1,187 +1,76 @@
#define __BSD_VISIBLE 1
#include "chaos-drop.h"
#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);
float fov_radians = fov_degrees * 3.14159 / 180;
/* Use FOV as the horizontal viewing angle */
float sd = (VWIDTH * HALF_SCREEN_HEIGHT / VHEIGHT) / tanf(fov_radians / 2);
/* The screen is at such a distance that 16 units is half the height. We
don't care where it is placed as we'll always send rays from the camera
itself. This is to ensure good ranges for fixed point values */
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.3;
}
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;
float mv;
/* Snap with closest direction */
if(fabsf(distance_nowrap) < fabsf(distance_wrap_up)) {
if(fabsf(distance_nowrap) < fabsf(distance_wrap_down))
mv = distance_nowrap * snap_factor;
else
mv = distance_wrap_down * snap_factor;
}
else {
if(fabsf(distance_wrap_down) < fabsf(distance_wrap_down))
mv = distance_wrap_down * snap_factor;
else
mv = distance_wrap_up * snap_factor;
}
camera->roll += mv;
if(camera->roll < 0)
camera->roll += 2 * M_PI;
else if(camera->roll > 2 * M_PI)
camera->roll -= 2 * M_PI;
/* Also rotate the player along with it */
float cos_mv, sin_mv;
sincosf(-mv, &sin_mv, &cos_mv);
num c = cos_mv, s = sin_mv;
num nx = camera->pos.x * c + camera->pos.z * s;
num nz = -camera->pos.x * s + camera->pos.z * c;
camera->pos.x = nx;
camera->pos.z = nz;
}
mat3 matrix_camera2world(struct camera const *camera)
{
num cos_r = camera->cos_r;
num sin_r = camera->sin_r;
num cos_p = camera->cos_p;
num sin_p = camera->sin_p;
num cos_y = camera->cos_y;
num sin_y = camera->sin_y;
#if 0
mat3 m_roll = {
cos_r, 0, sin_r,
0, 1, 0,
-sin_r, 0, cos_r,
};
mat3 m_pitch = {
1, 0, 0,
0, cos_p, +sin_p,
0, -sin_p, cos_p,
};
mat3 m_yaw = {
cos_y, +sin_y, 0,
-sin_y, cos_y, 0,
0, 0, 1,
};
mat3 M = m_roll * (m_pitch * m_yaw);
#endif
mat3 m_anti_roll = {
cos_r, 0, -sin_r,
0, 1, 0,
+sin_r, 0, cos_r,
};
mat3 m_anti_pitch = {
1, 0, 0,
0, cos_p, -sin_p,
0, +sin_p, cos_p,
};
mat3 m_anti_yaw = {
cos_y, -sin_y, 0,
+sin_y, cos_y, 0,
0, 0, 1,
};
return m_anti_yaw * (m_anti_pitch * m_anti_roll);
}
void camera_update_angles(struct camera *camera)
{
float c, s;
sincosf(camera->roll, &s, &c);
camera->sin_r = s;
camera->cos_r = c;
sincosf(camera->pitch, &s, &c);
camera->sin_p = s;
camera->cos_p = c;
sincosf(camera->yaw, &s, &c);
camera->sin_y = s;
camera->cos_y = c;
mat3 c2w = matrix_camera2world(camera);
camera->forward = c2w * vec3(0, 1, 0);
camera->right = c2w * vec3(1, 0, 0);
camera->up = c2w * vec3(0, 0, 1);
}
bool plane_collides(num16 x, num16 z, uint32_t shape)
{
int cx = ((x + num16(WORLD_SIZE / 2)) * num16(5.0 / WORLD_SIZE)).ifloor();
int cz = ((z + num16(WORLD_SIZE / 2)) * num16(5.0 / WORLD_SIZE)).ifloor();
return (unsigned)cx < 5 && (unsigned)cz < 5
&& ((shape >> (5*cz + 4-cx)) & 1);
}
using snum = num16;
using svec3 = vec<snum,3>;
/* Color palette for world objects */
static uint16_t const object_colors[] = {
RGB(3, 4, 8), /* Void/background */
RGB(3, 4, 8), /* Left wall (-X) */
RGB(5, 6, 9), /* Top wall (+Z) */
RGB(7, 8, 11), /* Right wall (+X) */
RGB(5, 6, 9), /* Bottom wall (-Z) */
RGB(31, 0, 0), /* ELEMENT_PLANE_DAMAGE */
RGB(0, 31, 0), /* ELEMENT_PLANE_ARROW */
RGB(15, 0, 0), /* ELEMENT_PLANE_INVIS */
};
/* Objects hit by a ray */
enum {
VOID = 0, /* Background (no hit) */
LEFT, TOP, RIGHT, BOTTOM, /* Wall sides */
OBJECT_PLANE, /* Object plane (from level) */
};
/* "Short" vec3: 16-bit-based vectors for faster raytracing */
using svec3 = vec<num16,3>;
svec3 svec3_of_vec3(vec3 v)
{
return svec3(snum(v.x), snum(v.y), snum(v.z));
return svec3(num16(v.x), num16(v.y), num16(v.z));
}
enum object {
/* Wall sides */
LEFT = 1, TOP, RIGHT, BOTTOM,
/* Object plane */
OBJECT_PLANE,
};
/* Cast a ray from `origin` with direction `rayDir`. Stores the distance to
the collision point in `*t` and the full-precision y-coordinate of that
point in `*collision_y`. Returns the identifier of the object type hit by
the ray. If `secondary` is true, invisible planes are not ignored. */
static int cast_ray(struct world const *world, svec3 const &origin,
svec3 const &rayDir, snum *t, num *collision_y, bool secondary)
svec3 const &rayDir, num16 *t, num *collision_y, bool secondary)
{
snum tx_rx_rz, tz_rx_rz;
/* We directly compute intersections between rays and walls by exploiting
the very regular structure of the walls. To find out whether the x or z
wall is hit first, we compute tx and tz (travel distances of the ray
before hitting the x/z walls) and check which is smaller.
The thing is, tx is Δx / rayDir.x and tz is Δz / rayDir.z. We don't want
to compute both divisions because it's expensive. So instead of tx < tz
we check tx * rayDir.x * rayDir.z < tz * rayDir.x * rayDir.z and only
divide once, when we know which is smaller. */
num16 tx_rx_rz, tz_rx_rz;
/* Object hit for each wall direction */
int hitx = 0, hitz = 0;
static snum const epsilon = 0.1;
/* Computing with num16 comes with range issues. Rays that go very straight
hit the x and z walls at more than 128 units away, causing an overflow
on the computation of the y coordinate. We hack around this by
registering background hits if the ray's x/z is smaller than epsilon. */
static num16 const epsilon = 0.1;
/* Determine which wall is hit first. */
if(rayDir.x > epsilon) {
tx_rx_rz = (snum(WORLD_SIZE / 2) - origin.x) * rayDir.z;
tx_rx_rz = (num16(WORLD_SIZE) - origin.x) * rayDir.z;
hitx = RIGHT;
}
else if(__builtin_expect(rayDir.x < -epsilon, 1)) {
tx_rx_rz = (snum(-WORLD_SIZE / 2) - origin.x) * rayDir.z;
tx_rx_rz = (num16(-WORLD_SIZE) - origin.x) * rayDir.z;
hitx = LEFT;
}
if(rayDir.z > epsilon) {
tz_rx_rz = (snum(WORLD_SIZE / 2) - origin.z) * rayDir.x;
tz_rx_rz = (num16(WORLD_SIZE) - origin.z) * rayDir.x;
hitz = TOP;
}
else if(__builtin_expect(rayDir.z < -epsilon, 1)) {
tz_rx_rz = (snum(-WORLD_SIZE / 2) - origin.z) * rayDir.x;
tz_rx_rz = (num16(-WORLD_SIZE) - origin.z) * rayDir.x;
hitz = BOTTOM;
}
@ -190,37 +79,39 @@ static int cast_ray(struct world const *world, svec3 const &origin,
if(hitz && (!hitx || (tz_rx_rz < tx_rx_rz) ^ rx_rz_sign)) {
if(hitz == TOP)
*t = num16::div_positive(snum(WORLD_SIZE / 2) - origin.z,
rayDir.z);
*t = num16::div_positive(num16(WORLD_SIZE) - origin.z, rayDir.z);
else
*t = num16::div_positive(origin.z + snum(WORLD_SIZE / 2),
-rayDir.z);
*t = num16::div_positive(origin.z + num16(WORLD_SIZE), -rayDir.z);
hit = hitz;
}
else if(__builtin_expect(hitx, 1)) {
if(hitx == RIGHT)
*t = num16::div_positive(snum(WORLD_SIZE / 2) - origin.x,
rayDir.x);
*t = num16::div_positive(num16(WORLD_SIZE) - origin.x, rayDir.x);
else
*t = num16::div_positive(origin.x + snum(WORLD_SIZE / 2),
-rayDir.x);
*t = num16::div_positive(origin.x + num16(WORLD_SIZE), -rayDir.x);
hit = hitx;
}
/* Compute collision point's y-coordinate in full precision. This only half
works because *t can be incorrect due to an overflow. Computing a more
precise version of *t would be too expensive though, so we settle for
this. */
*collision_y = hit ? num(origin.y) + num(*t) * num(rayDir.y) : num(192);
/* Compute intersection with object plane */
/* Determine if there is an intersection with the object plane */
if(__builtin_expect(
world->plane
&& (*collision_y > world->plane->y - world->depth)
&& (world->plane->type != ELEMENT_PLANE_INVIS || secondary),
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;
num16 plane_y = num16(world->plane->y - world->depth);
num16 plane_t = (plane_y - origin.y) / rayDir.y;
num16 plane_x = origin.x + plane_t * rayDir.x;
num16 plane_z = origin.z + plane_t * rayDir.z;
if(plane_collides(plane_x, plane_z, world->plane->shape)) {
if(level_plane_collides(plane_x, plane_z, world->plane->shape)) {
*t = plane_t;
*collision_y = world->plane->y - world->depth;
return OBJECT_PLANE;
@ -238,22 +129,22 @@ void render_fragment(struct camera const *camera, struct world const *world,
svec3 right = svec3_of_vec3(camera->right);
svec3 up = svec3_of_vec3(camera->up);
/* Screen center in front of the camera */
svec3 screen_center = origin + snum(camera->screen_distance) * forward;
/* Screen center (in front of the camera) */
svec3 screen_center = origin + num16(camera->screen_distance) * forward;
/* Unitary movements, in world coordinates, on screen placed in world,
corresponding to individual pixel sizes */
svec3 pixel_dy = snum(HALF_SCREEN_HEIGHT) * up / snum(VHEIGHT / 2);
svec3 pixel_dx = snum(HALF_SCREEN_HEIGHT) * right / snum(VHEIGHT / 2);
corresponding to individual pixels */
svec3 pixel_dy = num16(HALF_SCREEN_HEIGHT) * up / num16(VHEIGHT / 2);
svec3 pixel_dx = num16(HALF_SCREEN_HEIGHT) * right / num16(VHEIGHT / 2);
/* Ray direction at the start of current row */
svec3 rayDir_row = screen_center - origin
+ snum(VHEIGHT/2 - y_start) * pixel_dy
+ snum(0 - VWIDTH/2) * pixel_dx;
+ num16(VHEIGHT/2 - y_start) * pixel_dy
+ num16(0 - VWIDTH/2) * pixel_dx;
for(int y = y_start; y < y_start + y_height; y++) {
svec3 rayDir = rayDir_row;
for(int x = 0; x < VWIDTH; x++) {
snum t;
num16 t;
num coll_y;
int obj = cast_ray(world, origin, rayDir, &t, &coll_y, false);
bool darken = false;
@ -264,8 +155,8 @@ void render_fragment(struct camera const *camera, struct world const *world,
collision.z = origin.z + t * rayDir.z;
/* Mirror covers only part of width of wall */
bool ok_z = collision.z >= snum(-WORLD_SIZE / 4)
&& collision.z < snum(WORLD_SIZE / 4);
bool ok_z = collision.z >= num16(-WORLD_SIZE / 2)
&& collision.z < num16(WORLD_SIZE / 2);
/* Mirror also does not go on forever */
bool ok_y = world->mirror
&& coll_y >= (world->mirror->begin - world->depth)
@ -273,7 +164,7 @@ void render_fragment(struct camera const *camera, struct world const *world,
if(ok_z && ok_y) {
collision.x = origin.x + t * rayDir.x;
collision.y = snum(coll_y);
collision.y = num16(coll_y);
rayDir.x = -rayDir.x;
obj = cast_ray(world, collision, rayDir, &t, &coll_y,true);
darken = true;
@ -281,31 +172,21 @@ void render_fragment(struct camera const *camera, struct world const *world,
}
}
static uint16_t const colors[] = {
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), /* ELEMENT_PLANE_DAMAGE */
RGB(0, 31, 0), /* ELEMENT_PLANE_ARROW */
RGB(15, 0, 0), /* ELEMENT_PLANE_INVIS */
};
uint16_t color =
colors[obj + (obj == OBJECT_PLANE ? world->plane->type : 0)];
uint16_t color = object_colors[
obj + (obj == OBJECT_PLANE ? world->plane->type : 0)];
/* Don't show neons that are too far to avoid flickering */
if(coll_y < 64 && obj != OBJECT_PLANE) {
snum neon_pos = world->neon_position;
num16 neon_pos = world->neon_position;
if(obj == TOP || obj == BOTTOM)
neon_pos += world->neon_period * snum(0.5);
snum neon = snum(coll_y) - neon_pos;
neon_pos += world->neon_period * num16(0.5);
num16 neon = num16(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;
num16 neon_size = 1;
if(coll_y > 20)
neon_size += ((snum(coll_y)-snum(20)) * snum(1.0/4));
neon_size += ((num16(coll_y)-num16(20)) * num16(1.0/4));
if(neon <= neon_size)
color = world->text ? RGB(15,15,15) : 0xffff;
}

18
src/util.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <num/num.h>
#include <num/vec.h>
/* Export libnum symbols */
using namespace libnum;
/* Provide basic 3x3 matrix operations */
struct mat3
{
num x11, x12, x13;
num x21, x22, x23;
num x31, x32, x33;
};
mat3 operator *(mat3 const &A, mat3 const &B);
vec3 operator *(mat3 const &M, vec3 const &u);