Reworked the files before the big cleanup of the code.
This commit is contained in:
parent
6533f950d2
commit
8eae4b9420
30 changed files with 26 additions and 57 deletions
359
scratchpad/fenscaster.cpp
Normal file
359
scratchpad/fenscaster.cpp
Normal file
|
@ -0,0 +1,359 @@
|
|||
#include <fmt/core.h>
|
||||
#include <numbers>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "matrix.hpp"
|
||||
#include <cstdlib>
|
||||
#include "fenster/fenster.h"
|
||||
#include "dbc.hpp"
|
||||
|
||||
using matrix::Matrix;
|
||||
using namespace fmt;
|
||||
|
||||
Matrix MAP{
|
||||
{2,2,2,2,2,2,2,2,2},
|
||||
{2,0,8,0,0,0,0,0,2},
|
||||
{2,0,7,0,0,5,6,0,2},
|
||||
{2,0,0,0,0,0,0,0,2},
|
||||
{2,2,0,0,0,0,0,2,2},
|
||||
{2,0,0,1,3,4,0,0,2},
|
||||
{2,0,0,0,0,0,2,2,2},
|
||||
{2,2,2,2,2,2,2,2,2}
|
||||
};
|
||||
|
||||
const int SCREEN_HEIGHT=480;
|
||||
const int SCREEN_WIDTH=SCREEN_HEIGHT * 2;
|
||||
|
||||
const int THREED_VIEW_WIDTH=480;
|
||||
const int THREED_VIEW_HEIGHT=480;
|
||||
const int MAP_SIZE=matrix::width(MAP);
|
||||
const int TILE_SIZE=(SCREEN_WIDTH/2) / MAP_SIZE;
|
||||
const float FOV = std::numbers::pi / 3.0;
|
||||
const float HALF_FOV = FOV / 2;
|
||||
const int CASTED_RAYS=120;
|
||||
const float STEP_ANGLE = FOV / CASTED_RAYS;
|
||||
const int MAX_DEPTH = MAP_SIZE * TILE_SIZE;
|
||||
const float SCALE = (SCREEN_WIDTH / 2) / CASTED_RAYS;
|
||||
int PITCH=25;
|
||||
|
||||
float player_x = SCREEN_WIDTH / 4;
|
||||
float player_y = SCREEN_WIDTH / 4;
|
||||
|
||||
// x and y start position
|
||||
double posX = player_x / TILE_SIZE;
|
||||
double posY = player_y / TILE_SIZE;
|
||||
|
||||
// initial direction vector
|
||||
double dirX = -1;
|
||||
double dirY = 0;
|
||||
|
||||
// the 2d raycaster version of camera plane
|
||||
double planeX = 0;
|
||||
double planeY = 0.66;
|
||||
|
||||
#define rgba_color(r,g,b,a) (b<<(0*8))|(g<<(1*8))|(r<<(2*8))|(a<<(3*8))
|
||||
#define gray_color(c) rgba_color(c, c, c, 255)
|
||||
|
||||
std::vector<uint32_t> texture[8];
|
||||
#define texWidth 64
|
||||
#define texHeight 64
|
||||
|
||||
|
||||
void load_textures() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
texture[i].resize(texWidth * texHeight);
|
||||
}
|
||||
|
||||
for(int x = 0; x < texWidth; x++) {
|
||||
for(int y = 0; y < texHeight; y++)
|
||||
{
|
||||
int xorcolor = (x * 256 / texWidth) ^ (y * 256 / texHeight);
|
||||
//int xcolor = x * 256 / texWidth;
|
||||
int ycolor = y * 256 / texHeight;
|
||||
int xycolor = y * 128 / texHeight + x * 128 / texWidth;
|
||||
texture[0][texWidth * y + x] = 65536 * 254 * (x != y && x != texWidth - y); //flat red texture with black cross
|
||||
texture[1][texWidth * y + x] = xycolor + 256 * xycolor + 65536 * xycolor; //sloped greyscale
|
||||
texture[2][texWidth * y + x] = 256 * xycolor + 65536 * xycolor; //sloped yellow gradient
|
||||
texture[3][texWidth * y + x] = xorcolor + 256 * xorcolor + 65536 * xorcolor; //xor greyscale
|
||||
texture[4][texWidth * y + x] = 256 * xorcolor; //xor green
|
||||
texture[5][texWidth * y + x] = 65536 * 192 * (x % 16 && y % 16); //red bricks
|
||||
texture[6][texWidth * y + x] = 65536 * ycolor; //red gradient
|
||||
texture[7][texWidth * y + x] = 128 + 256 * 128 + 65536 * 128; //flat grey texture
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_rect(Fenster &window, Point pos, Point size, uint32_t color) {
|
||||
size_t x_start = size_t(pos.x);
|
||||
size_t y_start = size_t(pos.y);
|
||||
size_t width = size_t(size.x);
|
||||
size_t height = size_t(size.y);
|
||||
|
||||
dbc::check(x_start <= size_t(window.f.width), format("pos.x {} is greater than width {}", x_start, window.f.width));
|
||||
dbc::check(y_start <= size_t(window.f.height), format("pos.y {} is greater than height {}", y_start, window.f.height));
|
||||
dbc::check(x_start + width <= size_t(window.f.width), format("size width {} is greater than width {}", x_start + width, window.f.width));
|
||||
dbc::check(y_start + height <= size_t(window.f.height), format("size height {} is greater than height {}", y_start + height, window.f.height));
|
||||
|
||||
for(size_t y = y_start; y < y_start + height; y++) {
|
||||
for(size_t x = x_start; x < x_start + width; x++) {
|
||||
window.px(x, y) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_map_rect(Fenster &window, int x, int y, uint32_t color) {
|
||||
draw_rect(window,
|
||||
{size_t(x * TILE_SIZE), size_t(y * TILE_SIZE)},
|
||||
{size_t(TILE_SIZE-1), size_t(TILE_SIZE-1)},
|
||||
color);
|
||||
}
|
||||
|
||||
void draw_map(Fenster &window, Matrix &map) {
|
||||
uint32_t light_grey = gray_color(191);
|
||||
uint32_t dark_grey = gray_color(65);
|
||||
|
||||
for(size_t y = 0; y < matrix::height(map); y++) {
|
||||
for(size_t x = 0; x < matrix::width(map); x++) {
|
||||
draw_map_rect(window, x, y, map[y][x] == 0 ? dark_grey : light_grey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_line(Fenster &window, Point start, Point end, uint32_t color) {
|
||||
int x = int(start.x);
|
||||
int y = int(start.y);
|
||||
int x1 = int(end.x);
|
||||
int y1 = int(end.y);
|
||||
int dx = std::abs(x1 - x);
|
||||
int sx = x < x1 ? 1 : -1;
|
||||
int dy = std::abs(y1 - y) * -1;
|
||||
int sy = y < y1 ? 1 : -1;
|
||||
int error = dx + dy;
|
||||
|
||||
while(x != x1 || y != y1) {
|
||||
int e2 = 2 * error;
|
||||
|
||||
if(e2 >= dy) {
|
||||
error = error + dy;
|
||||
x = x + sx;
|
||||
}
|
||||
|
||||
if(e2 <= dx) {
|
||||
error = error + dx;
|
||||
y = y + sy;
|
||||
}
|
||||
|
||||
window.px(x, y) = color;
|
||||
}
|
||||
}
|
||||
|
||||
void clear(Fenster &window) {
|
||||
for(int y = 0; y < window.f.height; y++) {
|
||||
for(int x = 0; x < window.f.width; x++) {
|
||||
window.px(x, y) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_map_blocks(Fenster &window, int col, int row) {
|
||||
draw_map_rect(window, col, row, rgba_color(100, 20, 20, 255));
|
||||
}
|
||||
|
||||
void ray_casting(Fenster &window, Matrix& map) {
|
||||
int w = THREED_VIEW_WIDTH;
|
||||
int h = THREED_VIEW_HEIGHT;
|
||||
|
||||
for(int x = 0; x < w; x++) {
|
||||
// calculate ray position and direction
|
||||
double cameraX = 2 * x / double(w) - 1; // x-coord in camera space
|
||||
double rayDirX = dirX + planeX * cameraX;
|
||||
double rayDirY = dirY + planeY * cameraX;
|
||||
|
||||
// which box of the map we're in
|
||||
int mapX = int(posX);
|
||||
int mapY = int(posY);
|
||||
|
||||
// length of ray from current pos to next x or y-side
|
||||
double sideDistX;
|
||||
double sideDistY;
|
||||
|
||||
// length of ray from one x or y-side to next x or y-side
|
||||
double deltaDistX = std::abs(1.0 / rayDirX);
|
||||
double deltaDistY = std::abs(1.0 / rayDirY);
|
||||
double perpWallDist;
|
||||
|
||||
int stepX = 0;
|
||||
int stepY = 0;
|
||||
int hit = 0;
|
||||
int side = 0;
|
||||
|
||||
// calculate step and initial sideDist
|
||||
if(rayDirX < 0) {
|
||||
stepX = -1;
|
||||
sideDistX = (posX - mapX) * deltaDistX;
|
||||
} else {
|
||||
stepX = 1;
|
||||
sideDistX = (mapX + 1.0 - posX) * deltaDistX;
|
||||
}
|
||||
|
||||
if(rayDirY < 0) {
|
||||
stepY = -1;
|
||||
sideDistY = (posY - mapY) * deltaDistY;
|
||||
} else {
|
||||
stepY = 1;
|
||||
sideDistY = (mapY + 1.0 - posY) * deltaDistY;
|
||||
}
|
||||
|
||||
// perform DDA
|
||||
while(hit == 0) {
|
||||
if(sideDistX < sideDistY) {
|
||||
sideDistX += deltaDistX;
|
||||
mapX += stepX;
|
||||
side = 0;
|
||||
} else {
|
||||
sideDistY += deltaDistY;
|
||||
mapY += stepY;
|
||||
side = 1;
|
||||
}
|
||||
|
||||
if(map[mapY][mapX] > 0) hit = 1;
|
||||
}
|
||||
|
||||
if(side == 0) {
|
||||
perpWallDist = (sideDistX - deltaDistX);
|
||||
} else {
|
||||
perpWallDist = (sideDistY - deltaDistY);
|
||||
}
|
||||
|
||||
draw_map_blocks(window, mapX, mapY);
|
||||
|
||||
|
||||
// player direction ray
|
||||
draw_line(window, {size_t(posX * TILE_SIZE), size_t(posY * TILE_SIZE)},
|
||||
{(size_t)mapX * TILE_SIZE, (size_t)mapY * TILE_SIZE}, rgba_color(0, 255, 0, 255));
|
||||
|
||||
int lineHeight = int(h / perpWallDist);
|
||||
|
||||
int drawStart = -lineHeight / 2 + h / 2 + PITCH;
|
||||
if(drawStart < 0) drawStart = 0;
|
||||
|
||||
int drawEnd = lineHeight / 2 + h / 2 + PITCH;
|
||||
if(drawEnd >= h) drawEnd = h - 1;
|
||||
|
||||
int texNum = MAP[mapY][mapX] - 1;
|
||||
|
||||
// calculate value of wallX
|
||||
double wallX; // where exactly the wall was hit
|
||||
if(side == 0) {
|
||||
wallX = posY + perpWallDist * rayDirY;
|
||||
} else {
|
||||
wallX = posX + perpWallDist * rayDirX;
|
||||
}
|
||||
wallX -= floor((wallX));
|
||||
|
||||
// x coorindate on the texture
|
||||
int texX = int(wallX * double(texWidth));
|
||||
if(side == 0 && rayDirX > 0) texX = texWidth - texX - 1;
|
||||
if(side == 1 && rayDirY < 0) texX = texWidth - texX - 1;
|
||||
|
||||
// LODE: an integer-only bresenham or DDA like algorithm could make the texture coordinate stepping faster
|
||||
|
||||
// How much to increase the texture coordinate per screen pixel
|
||||
double step = 1.0 * texHeight / lineHeight;
|
||||
// Starting texture coordinate
|
||||
double texPos = (drawStart - PITCH - h / 2 + lineHeight / 2) * step;
|
||||
|
||||
for(int y = drawStart; y < drawEnd; y++) {
|
||||
// BUG? Why bitwise and here?
|
||||
int texY = (int)texPos & (texHeight - 1);
|
||||
texPos += step;
|
||||
uint32_t color = texture[texNum][texHeight * texY + texX];
|
||||
|
||||
if(side == 1) color = (color >> 1) & 8355711;
|
||||
window.px(x + THREED_VIEW_WIDTH, y) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_ceiling_floor(Fenster &window) {
|
||||
draw_rect(window,
|
||||
{size_t(window.width() / 2), size_t(window.height() / 2)},
|
||||
{size_t(window.width() / 2), size_t(window.height() / 2)},
|
||||
gray_color(200));
|
||||
|
||||
draw_rect(window,
|
||||
{size_t(window.width() / 2), 0},
|
||||
{size_t(window.height()), size_t(window.height() / 2 + PITCH)},
|
||||
gray_color(100));
|
||||
}
|
||||
|
||||
void draw_everything(Fenster &window) {
|
||||
clear(window);
|
||||
draw_map(window, MAP);
|
||||
draw_ceiling_floor(window);
|
||||
ray_casting(window, MAP);
|
||||
}
|
||||
|
||||
bool empty_space(int new_x, int new_y) {
|
||||
dbc::check((size_t)new_x < matrix::width(MAP),
|
||||
format("x={} too wide={}", new_x, matrix::width(MAP)));
|
||||
dbc::check((size_t)new_y < matrix::height(MAP),
|
||||
format("y={} too high={}", new_y, matrix::height(MAP)));
|
||||
|
||||
return MAP[new_y][new_x] == 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
Fenster window(SCREEN_WIDTH, SCREEN_HEIGHT, "Fenscaster");
|
||||
const int fps = 60;
|
||||
double moveSpeed = 0.1;
|
||||
double rotSpeed = 0.1;
|
||||
|
||||
load_textures();
|
||||
|
||||
while(window.loop(fps)) {
|
||||
draw_everything(window);
|
||||
|
||||
if(window.key('W')) {
|
||||
if(empty_space(int(posX + dirX * moveSpeed), int(posY))) posX += dirX * moveSpeed;
|
||||
if(empty_space(int(posX), int(posY + dirY * moveSpeed))) posY += dirY * moveSpeed;
|
||||
} else if(window.key('S')) {
|
||||
if(empty_space(int(posX - dirX * moveSpeed), int(posY))) posX -= dirX * moveSpeed;
|
||||
if(empty_space(int(posX), int(posY - dirY * moveSpeed))) posY -= dirY * moveSpeed;
|
||||
}
|
||||
|
||||
if(window.key('D')) {
|
||||
double oldDirX = dirX;
|
||||
dirX = dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed);
|
||||
dirY = oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed);
|
||||
|
||||
double oldPlaneX = planeX;
|
||||
planeX = planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed);
|
||||
planeY = oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed);
|
||||
} else if(window.key('A')) {
|
||||
double oldDirX = dirX;
|
||||
dirX = dirX * cos(rotSpeed) - dirY * sin(rotSpeed);
|
||||
dirY = oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed);
|
||||
|
||||
double oldPlaneX = planeX;
|
||||
planeX = planeX * cos(rotSpeed) - planeY * sin(rotSpeed);
|
||||
planeY = oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed);
|
||||
}
|
||||
|
||||
if(window.key('E')) {
|
||||
PITCH = std::clamp(PITCH + 10, -60, 240);
|
||||
} else if(window.key('Q')) {
|
||||
PITCH = std::clamp(PITCH - 10, -60, 240);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine,
|
||||
int nCmdShow) {
|
||||
(void)hInstance, (void)hPrevInstance, (void)pCmdLine, (void)nCmdShow;
|
||||
return main();
|
||||
}
|
||||
#endif
|
373
scratchpad/fenster/fenster.h
Normal file
373
scratchpad/fenster/fenster.h
Normal file
|
@ -0,0 +1,373 @@
|
|||
#ifndef FENSTER_H
|
||||
#define FENSTER_H
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include <objc/NSObjCRuntime.h>
|
||||
#include <objc/objc-runtime.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define _DEFAULT_SOURCE 1
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct fenster {
|
||||
const char *title;
|
||||
const int width;
|
||||
const int height;
|
||||
uint32_t *buf;
|
||||
int keys[256]; /* keys are mostly ASCII, but arrows are 17..20 */
|
||||
int mod; /* mod is 4 bits mask, ctrl=1, shift=2, alt=4, meta=8 */
|
||||
int x;
|
||||
int y;
|
||||
int mouse;
|
||||
#if defined(__APPLE__)
|
||||
id wnd;
|
||||
#elif defined(_WIN32)
|
||||
HWND hwnd;
|
||||
#else
|
||||
Display *dpy;
|
||||
Window w;
|
||||
GC gc;
|
||||
XImage *img;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef FENSTER_API
|
||||
#define FENSTER_API extern
|
||||
#endif
|
||||
FENSTER_API int fenster_open(struct fenster *f);
|
||||
FENSTER_API int fenster_loop(struct fenster *f);
|
||||
FENSTER_API void fenster_close(struct fenster *f);
|
||||
FENSTER_API void fenster_sleep(int64_t ms);
|
||||
FENSTER_API int64_t fenster_time(void);
|
||||
#define fenster_pixel(f, x, y) ((f)->buf[((y) * (f)->width) + (x)])
|
||||
|
||||
#ifndef FENSTER_HEADER
|
||||
#if defined(__APPLE__)
|
||||
#define msg(r, o, s) ((r(*)(id, SEL))objc_msgSend)(o, sel_getUid(s))
|
||||
#define msg1(r, o, s, A, a) \
|
||||
((r(*)(id, SEL, A))objc_msgSend)(o, sel_getUid(s), a)
|
||||
#define msg2(r, o, s, A, a, B, b) \
|
||||
((r(*)(id, SEL, A, B))objc_msgSend)(o, sel_getUid(s), a, b)
|
||||
#define msg3(r, o, s, A, a, B, b, C, c) \
|
||||
((r(*)(id, SEL, A, B, C))objc_msgSend)(o, sel_getUid(s), a, b, c)
|
||||
#define msg4(r, o, s, A, a, B, b, C, c, D, d) \
|
||||
((r(*)(id, SEL, A, B, C, D))objc_msgSend)(o, sel_getUid(s), a, b, c, d)
|
||||
|
||||
#define cls(x) ((id)objc_getClass(x))
|
||||
|
||||
extern id const NSDefaultRunLoopMode;
|
||||
extern id const NSApp;
|
||||
|
||||
static void fenster_draw_rect(id v, SEL s, CGRect r) {
|
||||
(void)r, (void)s;
|
||||
struct fenster *f = (struct fenster *)objc_getAssociatedObject(v, "fenster");
|
||||
CGContextRef context =
|
||||
msg(CGContextRef, msg(id, cls("NSGraphicsContext"), "currentContext"),
|
||||
"graphicsPort");
|
||||
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData(
|
||||
NULL, f->buf, f->width * f->height * 4, NULL);
|
||||
CGImageRef img =
|
||||
CGImageCreate(f->width, f->height, 8, 32, f->width * 4, space,
|
||||
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
|
||||
provider, NULL, false, kCGRenderingIntentDefault);
|
||||
CGColorSpaceRelease(space);
|
||||
CGDataProviderRelease(provider);
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, f->width, f->height), img);
|
||||
CGImageRelease(img);
|
||||
}
|
||||
|
||||
static BOOL fenster_should_close(id v, SEL s, id w) {
|
||||
(void)v, (void)s, (void)w;
|
||||
msg1(void, NSApp, "terminate:", id, NSApp);
|
||||
return YES;
|
||||
}
|
||||
|
||||
FENSTER_API int fenster_open(struct fenster *f) {
|
||||
msg(id, cls("NSApplication"), "sharedApplication");
|
||||
msg1(void, NSApp, "setActivationPolicy:", NSInteger, 0);
|
||||
f->wnd = msg4(id, msg(id, cls("NSWindow"), "alloc"),
|
||||
"initWithContentRect:styleMask:backing:defer:", CGRect,
|
||||
CGRectMake(0, 0, f->width, f->height), NSUInteger, 3,
|
||||
NSUInteger, 2, BOOL, NO);
|
||||
Class windelegate =
|
||||
objc_allocateClassPair((Class)cls("NSObject"), "FensterDelegate", 0);
|
||||
class_addMethod(windelegate, sel_getUid("windowShouldClose:"),
|
||||
(IMP)fenster_should_close, "c@:@");
|
||||
objc_registerClassPair(windelegate);
|
||||
msg1(void, f->wnd, "setDelegate:", id,
|
||||
msg(id, msg(id, (id)windelegate, "alloc"), "init"));
|
||||
Class c = objc_allocateClassPair((Class)cls("NSView"), "FensterView", 0);
|
||||
class_addMethod(c, sel_getUid("drawRect:"), (IMP)fenster_draw_rect, "i@:@@");
|
||||
objc_registerClassPair(c);
|
||||
|
||||
id v = msg(id, msg(id, (id)c, "alloc"), "init");
|
||||
msg1(void, f->wnd, "setContentView:", id, v);
|
||||
objc_setAssociatedObject(v, "fenster", (id)f, OBJC_ASSOCIATION_ASSIGN);
|
||||
|
||||
id title = msg1(id, cls("NSString"), "stringWithUTF8String:", const char *,
|
||||
f->title);
|
||||
msg1(void, f->wnd, "setTitle:", id, title);
|
||||
msg1(void, f->wnd, "makeKeyAndOrderFront:", id, nil);
|
||||
msg(void, f->wnd, "center");
|
||||
msg1(void, NSApp, "activateIgnoringOtherApps:", BOOL, YES);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FENSTER_API void fenster_close(struct fenster *f) {
|
||||
msg(void, f->wnd, "close");
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const uint8_t FENSTER_KEYCODES[128] = {65,83,68,70,72,71,90,88,67,86,0,66,81,87,69,82,89,84,49,50,51,52,54,53,61,57,55,45,56,48,93,79,85,91,73,80,10,76,74,39,75,59,92,44,47,78,77,46,9,32,96,8,0,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,2,3,127,0,5,0,4,0,20,19,18,17,0};
|
||||
// clang-format on
|
||||
FENSTER_API int fenster_loop(struct fenster *f) {
|
||||
msg1(void, msg(id, f->wnd, "contentView"), "setNeedsDisplay:", BOOL, YES);
|
||||
id ev = msg4(id, NSApp,
|
||||
"nextEventMatchingMask:untilDate:inMode:dequeue:", NSUInteger,
|
||||
NSUIntegerMax, id, NULL, id, NSDefaultRunLoopMode, BOOL, YES);
|
||||
if (!ev)
|
||||
return 0;
|
||||
NSUInteger evtype = msg(NSUInteger, ev, "type");
|
||||
switch (evtype) {
|
||||
case 1: /* NSEventTypeMouseDown */
|
||||
f->mouse |= 1;
|
||||
break;
|
||||
case 2: /* NSEventTypeMouseUp*/
|
||||
f->mouse &= ~1;
|
||||
break;
|
||||
case 5:
|
||||
case 6: { /* NSEventTypeMouseMoved */
|
||||
CGPoint xy = msg(CGPoint, ev, "locationInWindow");
|
||||
f->x = (int)xy.x;
|
||||
f->y = (int)(f->height - xy.y);
|
||||
return 0;
|
||||
}
|
||||
case 10: /*NSEventTypeKeyDown*/
|
||||
case 11: /*NSEventTypeKeyUp:*/ {
|
||||
NSUInteger k = msg(NSUInteger, ev, "keyCode");
|
||||
f->keys[k < 127 ? FENSTER_KEYCODES[k] : 0] = evtype == 10;
|
||||
NSUInteger mod = msg(NSUInteger, ev, "modifierFlags") >> 17;
|
||||
f->mod = (mod & 0xc) | ((mod & 1) << 1) | ((mod >> 1) & 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
msg1(void, NSApp, "sendEvent:", id, ev);
|
||||
return 0;
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
// clang-format off
|
||||
static const uint8_t FENSTER_KEYCODES[] = {0,27,49,50,51,52,53,54,55,56,57,48,45,61,8,9,81,87,69,82,84,89,85,73,79,80,91,93,10,0,65,83,68,70,71,72,74,75,76,59,39,96,0,92,90,88,67,86,66,78,77,44,46,47,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,17,3,0,20,0,19,0,5,18,4,26,127};
|
||||
// clang-format on
|
||||
typedef struct BINFO{
|
||||
BITMAPINFOHEADER bmiHeader;
|
||||
RGBQUAD bmiColors[3];
|
||||
}BINFO;
|
||||
static LRESULT CALLBACK fenster_wndproc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
struct fenster *f = (struct fenster *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
switch (msg) {
|
||||
case WM_PAINT: {
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(hwnd, &ps);
|
||||
HDC memdc = CreateCompatibleDC(hdc);
|
||||
HBITMAP hbmp = CreateCompatibleBitmap(hdc, f->width, f->height);
|
||||
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbmp);
|
||||
BINFO bi = {{sizeof(bi), f->width, -f->height, 1, 32, BI_BITFIELDS}};
|
||||
bi.bmiColors[0].rgbRed = 0xff;
|
||||
bi.bmiColors[1].rgbGreen = 0xff;
|
||||
bi.bmiColors[2].rgbBlue = 0xff;
|
||||
SetDIBitsToDevice(memdc, 0, 0, f->width, f->height, 0, 0, 0, f->height,
|
||||
f->buf, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
|
||||
BitBlt(hdc, 0, 0, f->width, f->height, memdc, 0, 0, SRCCOPY);
|
||||
SelectObject(memdc, oldbmp);
|
||||
DeleteObject(hbmp);
|
||||
DeleteDC(memdc);
|
||||
EndPaint(hwnd, &ps);
|
||||
} break;
|
||||
case WM_CLOSE:
|
||||
DestroyWindow(hwnd);
|
||||
break;
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
f->mouse = (msg == WM_LBUTTONDOWN);
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
f->y = HIWORD(lParam), f->x = LOWORD(lParam);
|
||||
break;
|
||||
case WM_KEYDOWN:
|
||||
case WM_KEYUP: {
|
||||
f->mod = ((GetKeyState(VK_CONTROL) & 0x8000) >> 15) |
|
||||
((GetKeyState(VK_SHIFT) & 0x8000) >> 14) |
|
||||
((GetKeyState(VK_MENU) & 0x8000) >> 13) |
|
||||
(((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) >> 12);
|
||||
f->keys[FENSTER_KEYCODES[HIWORD(lParam) & 0x1ff]] = !((lParam >> 31) & 1);
|
||||
} break;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FENSTER_API int fenster_open(struct fenster *f) {
|
||||
HINSTANCE hInstance = GetModuleHandle(NULL);
|
||||
WNDCLASSEX wc = {0};
|
||||
wc.cbSize = sizeof(WNDCLASSEX);
|
||||
wc.style = CS_VREDRAW | CS_HREDRAW;
|
||||
wc.lpfnWndProc = fenster_wndproc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpszClassName = f->title;
|
||||
RegisterClassEx(&wc);
|
||||
f->hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, f->title, f->title,
|
||||
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
f->width, f->height, NULL, NULL, hInstance, NULL);
|
||||
|
||||
if (f->hwnd == NULL)
|
||||
return -1;
|
||||
SetWindowLongPtr(f->hwnd, GWLP_USERDATA, (LONG_PTR)f);
|
||||
ShowWindow(f->hwnd, SW_NORMAL);
|
||||
UpdateWindow(f->hwnd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FENSTER_API void fenster_close(struct fenster *f) { (void)f; }
|
||||
|
||||
FENSTER_API int fenster_loop(struct fenster *f) {
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT)
|
||||
return -1;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
InvalidateRect(f->hwnd, NULL, TRUE);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
// clang-format off
|
||||
static int FENSTER_KEYCODES[124] = {XK_BackSpace,8,XK_Delete,127,XK_Down,18,XK_End,5,XK_Escape,27,XK_Home,2,XK_Insert,26,XK_Left,20,XK_Page_Down,4,XK_Page_Up,3,XK_Return,10,XK_Right,19,XK_Tab,9,XK_Up,17,XK_apostrophe,39,XK_backslash,92,XK_bracketleft,91,XK_bracketright,93,XK_comma,44,XK_equal,61,XK_grave,96,XK_minus,45,XK_period,46,XK_semicolon,59,XK_slash,47,XK_space,32,XK_a,65,XK_b,66,XK_c,67,XK_d,68,XK_e,69,XK_f,70,XK_g,71,XK_h,72,XK_i,73,XK_j,74,XK_k,75,XK_l,76,XK_m,77,XK_n,78,XK_o,79,XK_p,80,XK_q,81,XK_r,82,XK_s,83,XK_t,84,XK_u,85,XK_v,86,XK_w,87,XK_x,88,XK_y,89,XK_z,90,XK_0,48,XK_1,49,XK_2,50,XK_3,51,XK_4,52,XK_5,53,XK_6,54,XK_7,55,XK_8,56,XK_9,57};
|
||||
// clang-format on
|
||||
FENSTER_API int fenster_open(struct fenster *f) {
|
||||
f->dpy = XOpenDisplay(NULL);
|
||||
int screen = DefaultScreen(f->dpy);
|
||||
f->w = XCreateSimpleWindow(f->dpy, RootWindow(f->dpy, screen), 0, 0, f->width,
|
||||
f->height, 0, BlackPixel(f->dpy, screen),
|
||||
WhitePixel(f->dpy, screen));
|
||||
f->gc = XCreateGC(f->dpy, f->w, 0, 0);
|
||||
XSelectInput(f->dpy, f->w,
|
||||
ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask |
|
||||
ButtonReleaseMask | PointerMotionMask);
|
||||
XStoreName(f->dpy, f->w, f->title);
|
||||
XMapWindow(f->dpy, f->w);
|
||||
XSync(f->dpy, f->w);
|
||||
f->img = XCreateImage(f->dpy, DefaultVisual(f->dpy, 0), 24, ZPixmap, 0,
|
||||
(char *)f->buf, f->width, f->height, 32, 0);
|
||||
return 0;
|
||||
}
|
||||
FENSTER_API void fenster_close(struct fenster *f) { XCloseDisplay(f->dpy); }
|
||||
FENSTER_API int fenster_loop(struct fenster *f) {
|
||||
XEvent ev;
|
||||
XPutImage(f->dpy, f->w, f->gc, f->img, 0, 0, 0, 0, f->width, f->height);
|
||||
XFlush(f->dpy);
|
||||
while (XPending(f->dpy)) {
|
||||
XNextEvent(f->dpy, &ev);
|
||||
switch (ev.type) {
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
f->mouse = (ev.type == ButtonPress);
|
||||
break;
|
||||
case MotionNotify:
|
||||
f->x = ev.xmotion.x, f->y = ev.xmotion.y;
|
||||
break;
|
||||
case KeyPress:
|
||||
case KeyRelease: {
|
||||
int m = ev.xkey.state;
|
||||
int k = XkbKeycodeToKeysym(f->dpy, ev.xkey.keycode, 0, 0);
|
||||
for (unsigned int i = 0; i < 124; i += 2) {
|
||||
if (FENSTER_KEYCODES[i] == k) {
|
||||
f->keys[FENSTER_KEYCODES[i + 1]] = (ev.type == KeyPress);
|
||||
break;
|
||||
}
|
||||
}
|
||||
f->mod = (!!(m & ControlMask)) | (!!(m & ShiftMask) << 1) |
|
||||
(!!(m & Mod1Mask) << 2) | (!!(m & Mod4Mask) << 3);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
FENSTER_API void fenster_sleep(int64_t ms) { Sleep(ms); }
|
||||
FENSTER_API int64_t fenster_time() {
|
||||
LARGE_INTEGER freq, count;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&count);
|
||||
return (int64_t)(count.QuadPart * 1000.0 / freq.QuadPart);
|
||||
}
|
||||
#else
|
||||
FENSTER_API void fenster_sleep(int64_t ms) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
FENSTER_API int64_t fenster_time(void) {
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_REALTIME, &time);
|
||||
return time.tv_sec * 1000 + (time.tv_nsec / 1000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
class Fenster {
|
||||
public:
|
||||
struct fenster f;
|
||||
int64_t now;
|
||||
|
||||
Fenster(const int w, const int h, const char *title)
|
||||
: f{.title = title, .width = w, .height = h} {
|
||||
this->f.buf = new uint32_t[w * h];
|
||||
this->now = fenster_time();
|
||||
fenster_open(&this->f);
|
||||
}
|
||||
~Fenster() {
|
||||
fenster_close(&this->f);
|
||||
delete[] this->f.buf;
|
||||
}
|
||||
bool loop(const int fps) {
|
||||
int64_t t = fenster_time();
|
||||
if (t - this->now < 1000 / fps) {
|
||||
fenster_sleep(t - now);
|
||||
}
|
||||
this->now = t;
|
||||
return fenster_loop(&this->f) == 0;
|
||||
}
|
||||
inline uint32_t &px(const int x, const int y) {
|
||||
return fenster_pixel(&this->f, x, y);
|
||||
}
|
||||
bool key(int c) { return c >= 0 && c < 128 ? this->f.keys[c] : false; }
|
||||
int x() { return this->f.x; }
|
||||
int y() { return this->f.y; }
|
||||
int mouse() { return this->f.mouse; }
|
||||
int mod() { return this->f.mod; }
|
||||
int width() { return this->f.width; }
|
||||
int height() { return this->f.height; }
|
||||
};
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !FENSTER_HEADER */
|
||||
#endif /* FENSTER_H */
|
126
scratchpad/pycaster.py
Normal file
126
scratchpad/pycaster.py
Normal file
|
@ -0,0 +1,126 @@
|
|||
import pygame
|
||||
import sys
|
||||
import math
|
||||
|
||||
SCREEN_HEIGHT=480
|
||||
SCREEN_WIDTH=SCREEN_HEIGHT * 2
|
||||
MAP_SIZE=8
|
||||
TILE_SIZE=int((SCREEN_WIDTH / 2) / MAP_SIZE)
|
||||
FOV=math.pi / 3
|
||||
HALF_FOV = FOV / 2
|
||||
CASTED_RAYS=30
|
||||
STEP_ANGLE = FOV / CASTED_RAYS
|
||||
MAX_DEPTH = int(MAP_SIZE * TILE_SIZE)
|
||||
SCALE = (SCREEN_WIDTH / 2) / CASTED_RAYS
|
||||
|
||||
|
||||
player_x = (SCREEN_WIDTH/2)/2
|
||||
player_y = (SCREEN_WIDTH/2)/2
|
||||
player_angle = math.pi
|
||||
|
||||
MAP = ('########'
|
||||
'# # #'
|
||||
'# # ###'
|
||||
'# #'
|
||||
'## #'
|
||||
'# ### #'
|
||||
'# # #'
|
||||
'########')
|
||||
|
||||
pygame.init()
|
||||
win = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
||||
pygame.display.set_caption("Ray-Casting")
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
def draw_map():
|
||||
light_grey = (191, 191, 191)
|
||||
dark_grey = (65,65,65)
|
||||
|
||||
for i in range(MAP_SIZE):
|
||||
for j in range(MAP_SIZE):
|
||||
square = i * MAP_SIZE + j
|
||||
|
||||
pygame.draw.rect(win,
|
||||
light_grey if MAP[square] == '#' else dark_grey,
|
||||
(j * TILE_SIZE, i * TILE_SIZE, TILE_SIZE -1, TILE_SIZE - 1))
|
||||
|
||||
def ray_casting():
|
||||
# left angle of FOV
|
||||
start_angle = player_angle - HALF_FOV
|
||||
|
||||
for ray in range(CASTED_RAYS):
|
||||
for depth in range(1,MAX_DEPTH):
|
||||
target_x = player_x - math.sin(start_angle) * depth
|
||||
target_y = player_y + math.cos(start_angle) * depth
|
||||
col = int(target_x / TILE_SIZE)
|
||||
row = int(target_y / TILE_SIZE)
|
||||
square = row * MAP_SIZE + col
|
||||
|
||||
if MAP[square] == '#':
|
||||
pygame.draw.rect(win,
|
||||
(195, 137, 38),
|
||||
(col * TILE_SIZE,
|
||||
row * TILE_SIZE,
|
||||
TILE_SIZE -1, TILE_SIZE-1))
|
||||
|
||||
pygame.draw.line(win, (233, 166, 49),
|
||||
(player_x, player_y),
|
||||
(target_x, target_y))
|
||||
|
||||
# wall shading
|
||||
color = 255 / (1 + depth * depth * 0.0001)
|
||||
|
||||
# fix fish eye effect
|
||||
depth *= math.cos(player_angle - start_angle)
|
||||
|
||||
# calculate wall height
|
||||
wall_height = 21000 / (depth)
|
||||
|
||||
if wall_height > SCREEN_HEIGHT:
|
||||
wall_height = SCREEN_HEIGHT
|
||||
|
||||
pygame.draw.rect(win,
|
||||
(color, color, color),
|
||||
(SCREEN_HEIGHT + ray * SCALE,
|
||||
(SCREEN_HEIGHT / 2) - wall_height/2,
|
||||
SCALE, wall_height))
|
||||
|
||||
break
|
||||
|
||||
start_angle += STEP_ANGLE
|
||||
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit(0)
|
||||
|
||||
# update 2d background
|
||||
pygame.draw.rect(win, (0,0,0), (0, 0, SCREEN_HEIGHT, SCREEN_HEIGHT))
|
||||
|
||||
# update 3d background
|
||||
pygame.draw.rect(win, (100, 100, 100), (480, SCREEN_HEIGHT / 2, SCREEN_HEIGHT, SCREEN_HEIGHT))
|
||||
pygame.draw.rect(win, (200, 200, 200), (480, -SCREEN_HEIGHT / 2, SCREEN_HEIGHT, SCREEN_HEIGHT))
|
||||
|
||||
draw_map()
|
||||
ray_casting()
|
||||
|
||||
keys = pygame.key.get_pressed()
|
||||
if keys[pygame.K_LEFT]:
|
||||
# working with radians, not degrees
|
||||
player_angle -= 0.1
|
||||
elif keys[pygame.K_RIGHT]:
|
||||
player_angle += 0.1
|
||||
elif keys[pygame.K_UP]:
|
||||
forward = True
|
||||
player_x += -1 * math.sin(player_angle) * 5
|
||||
player_y += math.cos(player_angle) * 5
|
||||
elif keys[pygame.K_DOWN]:
|
||||
forward = False
|
||||
player_x -= -1 * math.sin(player_angle) * 5
|
||||
player_y -= math.cos(player_angle) * 5
|
||||
|
||||
# update the display
|
||||
pygame.display.flip()
|
||||
|
||||
clock.tick(30)
|
187
scratchpad/raycaster.cpp
Normal file
187
scratchpad/raycaster.cpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
#include <fmt/core.h>
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <numbers>
|
||||
#include <cmath>
|
||||
#include "matrix.hpp"
|
||||
#include <cstdlib>
|
||||
|
||||
using matrix::Matrix;
|
||||
using namespace fmt;
|
||||
|
||||
Matrix MAP{
|
||||
{1,1,1,1,1,1,1,1,1},
|
||||
{1,0,1,0,0,0,0,0,1},
|
||||
{1,0,1,0,0,1,1,0,1},
|
||||
{1,0,0,0,0,0,0,0,1},
|
||||
{1,1,0,0,0,0,0,0,1},
|
||||
{1,0,0,1,1,1,0,0,1},
|
||||
{1,0,0,0,1,0,0,0,1},
|
||||
{1,0,0,0,0,0,1,1,1},
|
||||
{1,1,1,1,1,1,1,1,1}
|
||||
};
|
||||
|
||||
const int SCREEN_HEIGHT=480;
|
||||
const int SCREEN_WIDTH=SCREEN_HEIGHT * 2;
|
||||
const int MAP_SIZE=matrix::width(MAP);
|
||||
const int TILE_SIZE=(SCREEN_WIDTH/2) / MAP_SIZE;
|
||||
const float FOV = std::numbers::pi / 3.0;
|
||||
const float HALF_FOV = FOV / 2;
|
||||
const int CASTED_RAYS=30;
|
||||
const float STEP_ANGLE = FOV / CASTED_RAYS;
|
||||
const int MAX_DEPTH = MAP_SIZE * TILE_SIZE;
|
||||
const float SCALE = (SCREEN_WIDTH / 2) / CASTED_RAYS;
|
||||
|
||||
float player_x = SCREEN_WIDTH / 4;
|
||||
float player_y = SCREEN_WIDTH / 4;
|
||||
float player_angle = std::numbers::pi;
|
||||
|
||||
void draw_rect(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, uint8_t color) {
|
||||
sf::RectangleShape rect(size);
|
||||
rect.setFillColor({color, color, color});
|
||||
rect.setPosition(pos);
|
||||
window.draw(rect);
|
||||
}
|
||||
|
||||
void draw_map_rect(sf::RenderWindow &window, int x, int y, uint8_t color) {
|
||||
draw_rect(window,
|
||||
{float(x * TILE_SIZE), float(y * TILE_SIZE)},
|
||||
{float(TILE_SIZE-1), float(TILE_SIZE-1)},
|
||||
color);
|
||||
}
|
||||
|
||||
void draw_map(sf::RenderWindow &window, Matrix &map) {
|
||||
uint8_t light_grey = 191;
|
||||
uint8_t dark_grey = 65;
|
||||
|
||||
for(size_t y = 0; y < matrix::height(map); y++) {
|
||||
for(size_t x = 0; x < matrix::width(map); x++) {
|
||||
draw_map_rect(window, x, y, map[y][x] == 1 ? light_grey : dark_grey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_line(sf::RenderWindow &window, sf::Vector2f start, sf::Vector2f end) {
|
||||
sf::Vertex line[] = {
|
||||
sf::Vertex(start),
|
||||
sf::Vertex(end)
|
||||
};
|
||||
|
||||
window.draw(line, 2, sf::Lines);
|
||||
}
|
||||
|
||||
void draw_map_rays(sf::RenderWindow &window, int col, int row, sf::Vector2f target) {
|
||||
draw_map_rect(window, col, row, 100);
|
||||
draw_line(window, {player_x, player_y}, target);
|
||||
}
|
||||
|
||||
void draw_3d_view(sf::RenderWindow &window, int depth, float start_angle, int ray) {
|
||||
uint8_t color = 255 / (1 + depth * depth * 0.0001);
|
||||
|
||||
float fixed_depth = depth * std::cos(player_angle - start_angle);
|
||||
|
||||
float wall_height = 21000 / fixed_depth;
|
||||
|
||||
if(wall_height > SCREEN_HEIGHT){
|
||||
wall_height = SCREEN_HEIGHT;
|
||||
}
|
||||
|
||||
draw_rect(window,
|
||||
{SCREEN_HEIGHT + ray * SCALE, (SCREEN_HEIGHT / 2) - wall_height / 2},
|
||||
{SCALE, wall_height},
|
||||
color);
|
||||
}
|
||||
|
||||
void ray_casting(sf::RenderWindow &window, Matrix& map) {
|
||||
float start_angle = player_angle - HALF_FOV;
|
||||
|
||||
for(int ray = 0; ray < CASTED_RAYS; ray++, start_angle += STEP_ANGLE)
|
||||
{
|
||||
for(int depth = 1; depth < MAX_DEPTH; depth++) {
|
||||
float target_x = player_x - std::sin(start_angle) * depth;
|
||||
float target_y = player_y + std::cos(start_angle) * depth;
|
||||
|
||||
int col = int(target_x / TILE_SIZE);
|
||||
int row = int(target_y / TILE_SIZE);
|
||||
|
||||
if(map[row][col] == 1) {
|
||||
draw_map_rays(window, col, row, {target_x, target_y});
|
||||
draw_3d_view(window, depth, start_angle, ray);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_ceiling_floor(sf::RenderWindow &window) {
|
||||
draw_rect(window,
|
||||
{SCREEN_HEIGHT, SCREEN_HEIGHT /2},
|
||||
{SCREEN_HEIGHT, SCREEN_HEIGHT},
|
||||
100);
|
||||
draw_rect(window,
|
||||
{SCREEN_HEIGHT, (SCREEN_HEIGHT * -1) / 2},
|
||||
{SCREEN_HEIGHT, SCREEN_HEIGHT},
|
||||
200);
|
||||
}
|
||||
|
||||
void draw_everything(sf::RenderWindow &window) {
|
||||
draw_map(window, MAP);
|
||||
draw_ceiling_floor(window);
|
||||
ray_casting(window, MAP);
|
||||
window.display();
|
||||
}
|
||||
|
||||
bool collision(float x, float y) {
|
||||
int col = int(x / TILE_SIZE);
|
||||
int row = int(y / TILE_SIZE);
|
||||
|
||||
return MAP[row][col] == 1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
using KB = sf::Keyboard;
|
||||
sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Raycaster");
|
||||
window.setVerticalSyncEnabled(true);
|
||||
|
||||
while(window.isOpen()) {
|
||||
draw_everything(window);
|
||||
|
||||
float x = player_x;
|
||||
float y = player_y;
|
||||
|
||||
if(KB::isKeyPressed(KB::Q)) {
|
||||
player_angle -= 0.1;
|
||||
} else if(KB::isKeyPressed(KB::E)) {
|
||||
player_angle += 0.1;
|
||||
}
|
||||
|
||||
if(KB::isKeyPressed(KB::W)) {
|
||||
x += -1 * std::sin(player_angle) * 5;
|
||||
y += std::cos(player_angle) * 5;
|
||||
} else if(KB::isKeyPressed(KB::S)) {
|
||||
x -= -1 * std::sin(player_angle) * 5;
|
||||
y -= std::cos(player_angle) * 5;
|
||||
}
|
||||
|
||||
if(KB::isKeyPressed(KB::D)) {
|
||||
x += -1 * std::sin(player_angle + std::numbers::pi * 0.5) * 5;
|
||||
y += std::cos(player_angle + std::numbers::pi * 0.5) * 5;
|
||||
} else if(KB::isKeyPressed(KB::A)) {
|
||||
x -= -1 * std::sin(player_angle + std::numbers::pi * 0.5) * 5;
|
||||
y -= std::cos(player_angle + std::numbers::pi * 0.5) * 5;
|
||||
}
|
||||
|
||||
if(!collision(x, y)) {
|
||||
player_x = x;
|
||||
player_y = y;
|
||||
}
|
||||
|
||||
sf::Event event;
|
||||
while(window.pollEvent(event)) {
|
||||
if(event.type == sf::Event::Closed) {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
228
scratchpad/raycaster_pixels.cpp
Normal file
228
scratchpad/raycaster_pixels.cpp
Normal file
|
@ -0,0 +1,228 @@
|
|||
#include <fmt/core.h>
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <numbers>
|
||||
#include <cmath>
|
||||
#include "matrix.hpp"
|
||||
#include <cstdlib>
|
||||
|
||||
using matrix::Matrix;
|
||||
using namespace fmt;
|
||||
|
||||
Matrix MAP{
|
||||
{1,1,1,1,1,1,1,1,1},
|
||||
{1,0,1,0,0,0,0,0,1},
|
||||
{1,0,1,0,0,1,1,0,1},
|
||||
{1,0,0,0,0,0,0,0,1},
|
||||
{1,1,0,0,0,0,0,0,1},
|
||||
{1,0,0,1,1,1,0,0,1},
|
||||
{1,0,0,0,1,0,0,0,1},
|
||||
{1,0,0,0,0,0,1,1,1},
|
||||
{1,1,1,1,1,1,1,1,1}
|
||||
};
|
||||
|
||||
const int SCREEN_HEIGHT=480;
|
||||
const int THREED_VIEW_WIDTH=480;
|
||||
const int THREED_VIEW_HEIGHT=480;
|
||||
const int SCREEN_WIDTH=SCREEN_HEIGHT * 2;
|
||||
const int MAP_SIZE=matrix::width(MAP);
|
||||
const int TILE_SIZE=(SCREEN_WIDTH/2) / MAP_SIZE;
|
||||
const float FOV = std::numbers::pi / 3.0;
|
||||
const float HALF_FOV = FOV / 2;
|
||||
const int CASTED_RAYS=120;
|
||||
const float STEP_ANGLE = FOV / CASTED_RAYS;
|
||||
const int MAX_DEPTH = MAP_SIZE * TILE_SIZE;
|
||||
const float SCALE = (SCREEN_WIDTH / 2) / CASTED_RAYS;
|
||||
|
||||
float player_x = SCREEN_WIDTH / 4;
|
||||
float player_y = SCREEN_WIDTH / 4;
|
||||
float player_angle = std::numbers::pi;
|
||||
|
||||
struct RGBA {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
|
||||
RGBA pixels[SCREEN_HEIGHT * SCREEN_HEIGHT] = {{0,0,0,0}};
|
||||
sf::Texture view_texture;
|
||||
sf::Sprite view_sprite;
|
||||
|
||||
void draw_rect(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, uint8_t color) {
|
||||
sf::RectangleShape rect(size);
|
||||
rect.setFillColor({color, color, color});
|
||||
rect.setPosition(pos);
|
||||
window.draw(rect);
|
||||
}
|
||||
|
||||
void draw_pixel_buffer(sf::RenderWindow &window) {
|
||||
view_texture.update((uint8_t *)pixels, SCREEN_HEIGHT, SCREEN_HEIGHT, 0, 0);
|
||||
view_sprite.setTexture(view_texture);
|
||||
view_sprite.setPosition(SCREEN_HEIGHT, 0);
|
||||
window.draw(view_sprite);
|
||||
}
|
||||
|
||||
void draw_pixel_rect(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, uint8_t color) {
|
||||
size_t x_start = size_t(pos.x - SCREEN_HEIGHT);
|
||||
size_t y_start = size_t(pos.y);
|
||||
size_t width = size_t(size.x);
|
||||
size_t height = size_t(size.y);
|
||||
|
||||
for(size_t y = y_start; y < y_start + height; y++) {
|
||||
for(size_t x = x_start; x < x_start + width; x++) {
|
||||
size_t pixel_index = (y * SCREEN_HEIGHT) + x;
|
||||
pixels[pixel_index] = {color, color, color, 255};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_map_rect(sf::RenderWindow &window, int x, int y, uint8_t color) {
|
||||
draw_rect(window,
|
||||
{float(x * TILE_SIZE), float(y * TILE_SIZE)},
|
||||
{float(TILE_SIZE-1), float(TILE_SIZE-1)},
|
||||
color);
|
||||
}
|
||||
|
||||
void draw_map(sf::RenderWindow &window, Matrix &map) {
|
||||
uint8_t light_grey = 191;
|
||||
uint8_t dark_grey = 65;
|
||||
|
||||
for(size_t y = 0; y < matrix::height(map); y++) {
|
||||
for(size_t x = 0; x < matrix::width(map); x++) {
|
||||
draw_map_rect(window, x, y, map[y][x] == 1 ? light_grey : dark_grey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_line(sf::RenderWindow &window, sf::Vector2f start, sf::Vector2f end) {
|
||||
sf::Vertex line[] = {
|
||||
sf::Vertex(start),
|
||||
sf::Vertex(end)
|
||||
};
|
||||
|
||||
window.draw(line, 2, sf::Lines);
|
||||
}
|
||||
|
||||
void draw_map_rays(sf::RenderWindow &window, int col, int row, sf::Vector2f target) {
|
||||
draw_map_rect(window, col, row, 100);
|
||||
draw_line(window, {player_x, player_y}, target);
|
||||
}
|
||||
|
||||
void draw_3d_view(sf::RenderWindow &window, int depth, float start_angle, int ray) {
|
||||
uint8_t color = 255 / (1 + depth * depth * 0.0001);
|
||||
|
||||
float fixed_depth = depth * std::cos(player_angle - start_angle);
|
||||
|
||||
float wall_height = 21000 / fixed_depth;
|
||||
|
||||
if(wall_height > SCREEN_HEIGHT){
|
||||
wall_height = SCREEN_HEIGHT;
|
||||
}
|
||||
|
||||
draw_pixel_rect(window,
|
||||
{SCREEN_HEIGHT + ray * SCALE, (SCREEN_HEIGHT / 2) - wall_height / 2},
|
||||
{SCALE, wall_height},
|
||||
color);
|
||||
}
|
||||
|
||||
void clear_pixel_buffer() {
|
||||
std::fill_n(pixels, SCREEN_HEIGHT * SCREEN_HEIGHT, RGBA{});
|
||||
}
|
||||
|
||||
void ray_casting(sf::RenderWindow &window, Matrix& map) {
|
||||
clear_pixel_buffer();
|
||||
float start_angle = player_angle - HALF_FOV;
|
||||
|
||||
for(int ray = 0; ray < CASTED_RAYS; ray++, start_angle += STEP_ANGLE)
|
||||
{
|
||||
for(int depth = 1; depth < MAX_DEPTH; depth++) {
|
||||
float target_x = player_x - std::sin(start_angle) * depth;
|
||||
float target_y = player_y + std::cos(start_angle) * depth;
|
||||
|
||||
int col = int(target_x / TILE_SIZE);
|
||||
int row = int(target_y / TILE_SIZE);
|
||||
|
||||
if(map[row][col] == 1) {
|
||||
draw_map_rays(window, col, row, {target_x, target_y});
|
||||
draw_3d_view(window, depth, start_angle, ray);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_ceiling_floor(sf::RenderWindow &window) {
|
||||
draw_rect(window,
|
||||
{SCREEN_HEIGHT, SCREEN_HEIGHT /2},
|
||||
{SCREEN_HEIGHT, SCREEN_HEIGHT},
|
||||
100);
|
||||
draw_rect(window,
|
||||
{SCREEN_HEIGHT, (SCREEN_HEIGHT * -1) / 2},
|
||||
{SCREEN_HEIGHT, SCREEN_HEIGHT},
|
||||
200);
|
||||
}
|
||||
|
||||
void draw_everything(sf::RenderWindow &window) {
|
||||
draw_map(window, MAP);
|
||||
draw_ceiling_floor(window);
|
||||
ray_casting(window, MAP);
|
||||
draw_pixel_buffer(window);
|
||||
window.display();
|
||||
}
|
||||
|
||||
bool collision(float x, float y) {
|
||||
int col = int(x / TILE_SIZE);
|
||||
int row = int(y / TILE_SIZE);
|
||||
|
||||
return MAP[row][col] == 1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
using KB = sf::Keyboard;
|
||||
sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Raycaster");
|
||||
window.setVerticalSyncEnabled(true);
|
||||
view_texture.create(SCREEN_HEIGHT, SCREEN_HEIGHT);
|
||||
|
||||
while(window.isOpen()) {
|
||||
draw_everything(window);
|
||||
|
||||
float x = player_x;
|
||||
float y = player_y;
|
||||
|
||||
if(KB::isKeyPressed(KB::Q)) {
|
||||
player_angle -= 0.1;
|
||||
} else if(KB::isKeyPressed(KB::E)) {
|
||||
player_angle += 0.1;
|
||||
}
|
||||
|
||||
if(KB::isKeyPressed(KB::W)) {
|
||||
x += -1 * std::sin(player_angle) * 5;
|
||||
y += std::cos(player_angle) * 5;
|
||||
} else if(KB::isKeyPressed(KB::S)) {
|
||||
x -= -1 * std::sin(player_angle) * 5;
|
||||
y -= std::cos(player_angle) * 5;
|
||||
}
|
||||
|
||||
if(KB::isKeyPressed(KB::D)) {
|
||||
x += -1 * std::sin(player_angle + std::numbers::pi * 0.5) * 5;
|
||||
y += std::cos(player_angle + std::numbers::pi * 0.5) * 5;
|
||||
} else if(KB::isKeyPressed(KB::A)) {
|
||||
x -= -1 * std::sin(player_angle + std::numbers::pi * 0.5) * 5;
|
||||
y -= std::cos(player_angle + std::numbers::pi * 0.5) * 5;
|
||||
}
|
||||
|
||||
if(!collision(x, y)) {
|
||||
player_x = x;
|
||||
player_y = y;
|
||||
}
|
||||
|
||||
sf::Event event;
|
||||
while(window.pollEvent(event)) {
|
||||
if(event.type == sf::Event::Closed) {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -135,14 +135,14 @@ int main(int /*argc*/, char */*argv*/[])
|
|||
|
||||
//load some textures
|
||||
unsigned long tw, th, error = 0;
|
||||
error |= loadImage(texture[0], tw, th, "pics/eagle.png");
|
||||
error |= loadImage(texture[1], tw, th, "pics/redbrick.png");
|
||||
error |= loadImage(texture[2], tw, th, "pics/purplestone.png");
|
||||
error |= loadImage(texture[3], tw, th, "pics/greystone.png");
|
||||
error |= loadImage(texture[4], tw, th, "pics/bluestone.png");
|
||||
error |= loadImage(texture[5], tw, th, "pics/mossy.png");
|
||||
error |= loadImage(texture[6], tw, th, "pics/wood.png");
|
||||
error |= loadImage(texture[7], tw, th, "pics/colorstone.png");
|
||||
error |= loadImage(texture[0], tw, th, "assets/eagle.png");
|
||||
error |= loadImage(texture[1], tw, th, "assets/redbrick.png");
|
||||
error |= loadImage(texture[2], tw, th, "assets/purplestone.png");
|
||||
error |= loadImage(texture[3], tw, th, "assets/greystone.png");
|
||||
error |= loadImage(texture[4], tw, th, "assets/bluestone.png");
|
||||
error |= loadImage(texture[5], tw, th, "assets/mossy.png");
|
||||
error |= loadImage(texture[6], tw, th, "assets/wood.png");
|
||||
error |= loadImage(texture[7], tw, th, "assets/colorstone.png");
|
||||
if(error) { std::cout << "error loading images" << std::endl; return 1; }
|
||||
|
||||
//load some sprite textures
|
||||
|
|
|
@ -107,14 +107,14 @@ int main(int /*argc*/, char */*argv*/[])
|
|||
#else
|
||||
//generate some textures
|
||||
unsigned long tw, th;
|
||||
loadImage(texture[0], tw, th, "pics/eagle.png");
|
||||
loadImage(texture[1], tw, th, "pics/redbrick.png");
|
||||
loadImage(texture[2], tw, th, "pics/purplestone.png");
|
||||
loadImage(texture[3], tw, th, "pics/greystone.png");
|
||||
loadImage(texture[4], tw, th, "pics/bluestone.png");
|
||||
loadImage(texture[5], tw, th, "pics/mossy.png");
|
||||
loadImage(texture[6], tw, th, "pics/wood.png");
|
||||
loadImage(texture[7], tw, th, "pics/colorstone.png");
|
||||
loadImage(texture[0], tw, th, "assets/eagle.png");
|
||||
loadImage(texture[1], tw, th, "assets/redbrick.png");
|
||||
loadImage(texture[2], tw, th, "assets/purplestone.png");
|
||||
loadImage(texture[3], tw, th, "assets/greystone.png");
|
||||
loadImage(texture[4], tw, th, "assets/bluestone.png");
|
||||
loadImage(texture[5], tw, th, "assets/mossy.png");
|
||||
loadImage(texture[6], tw, th, "assets/wood.png");
|
||||
loadImage(texture[7], tw, th, "assets/colorstone.png");
|
||||
#endif
|
||||
|
||||
//start the main loop
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue