clean up all code but main.cc + SDL version
This commit is contained in:
parent
2437e4abfc
commit
7c52c73716
|
@ -9,6 +9,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
|||
|
||||
set(SOURCES
|
||||
src/main.cc
|
||||
src/game.cc
|
||||
src/raytracing.cc)
|
||||
set(ASSETS)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
139
src/chaos-drop.h
139
src/chaos-drop.h
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue