244 lines
7.2 KiB
C++
244 lines
7.2 KiB
C++
#define __BSD_VISIBLE 1
|
|
#include "chaos-drop.h"
|
|
#include <stdio.h>
|
|
#include <math.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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
using snum = num16;
|
|
using svec3 = vec<snum,3>;
|
|
svec3 svec3_of_vec3(vec3 v)
|
|
{
|
|
return svec3(snum(v.x), snum(v.y), snum(v.z));
|
|
}
|
|
|
|
enum object {
|
|
/* Wall sides */
|
|
LEFT = 1, TOP, RIGHT, BOTTOM,
|
|
};
|
|
|
|
static int cast_ray(svec3 const &origin, svec3 const &rayDir, snum *t)
|
|
{
|
|
snum tx_rx_rz, tz_rx_rz;
|
|
int hitx = 0, hitz = 0;
|
|
|
|
if(rayDir.x > 0) {
|
|
tx_rx_rz = (snum(WORLD_SIZE / 2) - origin.x) * rayDir.z;
|
|
hitx = RIGHT;
|
|
}
|
|
else if(rayDir.x < 0) {
|
|
tx_rx_rz = (snum(-WORLD_SIZE / 2) - origin.x) * rayDir.z;
|
|
hitx = LEFT;
|
|
}
|
|
|
|
if(rayDir.z > 0) {
|
|
tz_rx_rz = (snum(WORLD_SIZE / 2) - origin.z) * rayDir.x;
|
|
hitz = TOP;
|
|
}
|
|
else if(rayDir.z < 0) {
|
|
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);
|
|
|
|
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;
|
|
}
|
|
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);
|
|
}
|
|
return hitx;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void render_fragment(struct camera const *camera, uint16_t *fragment,
|
|
int y_start, int y_height)
|
|
{
|
|
svec3 origin = svec3_of_vec3(camera->pos);
|
|
svec3 forward = svec3_of_vec3(camera->forward);
|
|
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;
|
|
/* 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);
|
|
|
|
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;
|
|
|
|
if(obj == LEFT || obj == RIGHT) {
|
|
svec3 collision;
|
|
collision.z = origin.z + t * rayDir.z;
|
|
|
|
if(collision.z >= snum(-WORLD_SIZE / 4)
|
|
&& collision.z < snum(WORLD_SIZE / 4)) {
|
|
collision.x = origin.x + t * rayDir.x;
|
|
collision.y = origin.y + t * rayDir.y;
|
|
rayDir.x = -rayDir.x;
|
|
obj = cast_ray(collision, rayDir, &t);
|
|
recolor = 1 + (obj == RIGHT);
|
|
collision_y = collision.y + t * rayDir.y;
|
|
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,
|
|
};
|
|
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(obj == TOP || obj == BOTTOM)
|
|
neon_pos += snum(16);
|
|
snum neon = collision_y - neon_pos;
|
|
neon.v = neon.v & (snum(32).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(neon <= neon_size)
|
|
color = 0xffff;
|
|
}
|
|
|
|
if(recolor == 1)
|
|
color = ((color & 0xf7de) >> 1) + (0xf000 >> 1);
|
|
if(recolor == 2)
|
|
color = ((color & 0xf7de) >> 1) + (0x001f >> 1);
|
|
|
|
*fragment++ = color;
|
|
rayDir += pixel_dx;
|
|
}
|
|
|
|
rayDir_row -= pixel_dy;
|
|
}
|
|
|
|
// printf("%f %f %f\n", (float)rayDir.x, (float)rayDir.y, (float)rayDir.z);
|
|
}
|