First cut at a replica of the python raycaster. Left side almost works the same but have to sort out math differences.
parent
6b181382bd
commit
ca80736d7c
@ -0,0 +1,40 @@ |
||||
#include "dbc.hpp" |
||||
|
||||
void dbc::log(const string &message) { |
||||
fmt::print("{}\n", message); |
||||
} |
||||
|
||||
void dbc::sentinel(const string &message) { |
||||
string err = fmt::format("[SENTINEL!] {}\n", message); |
||||
throw dbc::SentinelError{err}; |
||||
} |
||||
|
||||
void dbc::pre(const string &message, bool test) { |
||||
if(!test) { |
||||
string err = fmt::format("[PRE!] {}\n", message); |
||||
throw dbc::PreCondError{err}; |
||||
} |
||||
} |
||||
|
||||
void dbc::pre(const string &message, std::function<bool()> tester) { |
||||
dbc::pre(message, tester()); |
||||
} |
||||
|
||||
void dbc::post(const string &message, bool test) { |
||||
if(!test) { |
||||
string err = fmt::format("[POST!] {}\n", message); |
||||
throw dbc::PostCondError{err}; |
||||
} |
||||
} |
||||
|
||||
void dbc::post(const string &message, std::function<bool()> tester) { |
||||
dbc::post(message, tester()); |
||||
} |
||||
|
||||
void dbc::check(bool test, const string &message) { |
||||
if(!test) { |
||||
string err = fmt::format("[CHECK!] {}\n", message); |
||||
fmt::println("{}", err); |
||||
throw dbc::CheckError{err}; |
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
#pragma once |
||||
|
||||
#include <string> |
||||
#include <fmt/core.h> |
||||
#include <functional> |
||||
|
||||
using std::string; |
||||
|
||||
namespace dbc { |
||||
class Error { |
||||
public: |
||||
const string message; |
||||
Error(string m) : message{m} {} |
||||
Error(const char *m) : message{m} {} |
||||
}; |
||||
|
||||
class CheckError : public Error {}; |
||||
class SentinelError : public Error {}; |
||||
class PreCondError : public Error {}; |
||||
class PostCondError : public Error {}; |
||||
|
||||
void log(const string &message); |
||||
void sentinel(const string &message); |
||||
void pre(const string &message, bool test); |
||||
void pre(const string &message, std::function<bool()> tester); |
||||
void post(const string &message, bool test); |
||||
void post(const string &message, std::function<bool()> tester); |
||||
void check(bool test, const string &message); |
||||
} |
@ -1,34 +0,0 @@ |
||||
/*
|
||||
Copyright (c) 2004, Lode Vandevenne |
||||
All rights reserved. |
||||
*/ |
||||
|
||||
#include <cmath> |
||||
#include <string> |
||||
#include <vector> |
||||
#include <iostream> |
||||
|
||||
#include "quickcg.h" |
||||
using namespace QuickCG; |
||||
using namespace std; |
||||
|
||||
//place the example code below here:
|
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
screen(256, 256, 0, "Small Test Script"); |
||||
for (int x = 0; x < w; x++) |
||||
for (int y = 0; y < h; y++) |
||||
{ |
||||
pset(x, y, ColorRGBA(x, y, 128, 255)); |
||||
} |
||||
print("Hello, world!", 8, 8); |
||||
|
||||
std::string test; |
||||
test.resize(20); |
||||
|
||||
redraw(); |
||||
sleep(); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,94 @@ |
||||
#include "matrix.hpp" |
||||
#include "dbc.hpp" |
||||
#include <fmt/core.h> |
||||
#include <cmath> |
||||
#include <cstdlib> |
||||
|
||||
using namespace fmt; |
||||
using std::min, std::max; |
||||
|
||||
namespace matrix { |
||||
|
||||
flood::flood(Matrix &mat, Point start, int old_val, int new_val) : |
||||
mat(mat), start(start), old_val(old_val), new_val(new_val), |
||||
x(start.x), y(start.y), dirs{mat, start.x, start.y} |
||||
{ |
||||
dbc::check(old_val != new_val, "what you doing?"); |
||||
current_loc = start; |
||||
q.push(start); |
||||
} |
||||
|
||||
bool flood::next() { |
||||
if(!q.empty()) { |
||||
if(!dirs.next()) { |
||||
// box is done reset it
|
||||
auto current_loc = q.front(); |
||||
q.pop(); |
||||
|
||||
dirs = matrix::compass{mat, current_loc.x, current_loc.y}; |
||||
dirs.next(); |
||||
} |
||||
|
||||
// get the next thing
|
||||
if(mat[dirs.y][dirs.x] <= old_val) { |
||||
mat[dirs.y][dirs.x] = new_val; |
||||
x = dirs.x; |
||||
y = dirs.y; |
||||
|
||||
q.push({.x=dirs.x, .y=dirs.y}); |
||||
} |
||||
|
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
line::line(Point start, Point end) : |
||||
x(start.x), y(start.y), |
||||
x1(end.x), y1(end.y) |
||||
{ |
||||
dx = std::abs(x1 - x); |
||||
sx = x < x1 ? 1 : -1; |
||||
dy = std::abs(y1 - y) * -1; |
||||
sy = y < y1 ? 1 : -1; |
||||
error = dx + dy; |
||||
} |
||||
|
||||
bool line::next() { |
||||
if(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; |
||||
} |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
void dump(const std::string &msg, Matrix &map, int show_x, int show_y) { |
||||
println("----------------- {}", msg); |
||||
|
||||
for(each_row it{map}; it.next();) { |
||||
int cell = map[it.y][it.x]; |
||||
|
||||
if(int(it.x) == show_x && int(it.y) == show_y) { |
||||
print("{:x}<", cell); |
||||
} else if(cell > 15) { |
||||
print("* "); |
||||
} else { |
||||
print("{:x} ", cell); |
||||
} |
||||
|
||||
if(it.row) print("\n"); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,305 @@ |
||||
#pragma once |
||||
#include <vector> |
||||
#include <queue> |
||||
#include <string> |
||||
#include <array> |
||||
#include <fmt/core.h> |
||||
#include "point.hpp" |
||||
|
||||
namespace matrix { |
||||
using std::vector, std::queue, std::array; |
||||
using std::min, std::max, std::floor; |
||||
|
||||
typedef vector<int> Row; |
||||
typedef vector<Row> Matrix; |
||||
|
||||
/*
|
||||
* Just a quick thing to reset a matrix to a value. |
||||
*/ |
||||
template<typename MAT, typename VAL> |
||||
inline void assign(MAT &out, VAL new_value) { |
||||
for(auto &row : out) { |
||||
row.assign(row.size(), new_value); |
||||
} |
||||
} |
||||
|
||||
template<typename MAT> |
||||
inline bool inbounds(MAT &mat, size_t x, size_t y) { |
||||
// since Point.x and Point.y are size_t any negatives are massive
|
||||
bool res = (y < mat.size()) && (x < mat[0].size()); |
||||
return res; |
||||
} |
||||
|
||||
template<typename MAT> |
||||
inline size_t width(MAT &mat) { |
||||
return mat[0].size(); |
||||
} |
||||
|
||||
template<typename MAT> |
||||
inline size_t height(MAT &mat) { |
||||
return mat.size(); |
||||
} |
||||
|
||||
inline size_t next_x(size_t x, size_t width) { |
||||
return (x + 1) * ((x + 1) < width); |
||||
} |
||||
|
||||
inline size_t next_y(size_t x, size_t y) { |
||||
return y + (x == 0); |
||||
} |
||||
|
||||
inline bool at_end(size_t y, size_t height) { |
||||
return y < height; |
||||
} |
||||
|
||||
inline bool end_row(size_t x, size_t width) { |
||||
return x == width - 1; |
||||
} |
||||
|
||||
void dump(const std::string &msg, Matrix &map, int show_x=-1, int show_y=-1); |
||||
|
||||
template<typename MAT> |
||||
struct each_cell_t { |
||||
size_t x = ~0; |
||||
size_t y = ~0; |
||||
size_t width = 0; |
||||
size_t height = 0; |
||||
|
||||
each_cell_t(MAT &mat) |
||||
{ |
||||
height = matrix::height(mat); |
||||
width = matrix::width(mat); |
||||
} |
||||
|
||||
bool next() { |
||||
x = next_x(x, width); |
||||
y = next_y(x, y); |
||||
return at_end(y, height); |
||||
} |
||||
}; |
||||
|
||||
template<typename MAT> |
||||
struct viewport_t { |
||||
Point start; |
||||
// this is the point in the map
|
||||
size_t x; |
||||
size_t y; |
||||
// this is the point inside the box, start at 0
|
||||
size_t view_x = ~0; |
||||
size_t view_y = ~0; |
||||
// viewport width/height
|
||||
size_t width; |
||||
size_t height; |
||||
|
||||
viewport_t(MAT &mat, Point start, int max_x, int max_y) : |
||||
start(start), |
||||
x(start.x-1), |
||||
y(start.y-1) |
||||
{ |
||||
width = std::min(size_t(max_x), matrix::width(mat) - start.x); |
||||
height = std::min(size_t(max_y), matrix::height(mat) - start.y); |
||||
fmt::println("viewport_t max_x, max_y {},{} vs matrix {},{}, x={}, y={}", |
||||
max_x, max_y, matrix::width(mat), matrix::height(mat), x, y); |
||||
} |
||||
|
||||
bool next() { |
||||
y = next_y(x, y); |
||||
x = next_x(x, width); |
||||
view_x = next_x(view_x, width); |
||||
view_y = next_y(view_x, view_y); |
||||
return at_end(y, height); |
||||
} |
||||
}; |
||||
|
||||
using viewport = viewport_t<Matrix>; |
||||
|
||||
using each_cell = each_cell_t<Matrix>; |
||||
|
||||
template<typename MAT> |
||||
struct each_row_t { |
||||
size_t x = ~0; |
||||
size_t y = ~0; |
||||
size_t width = 0; |
||||
size_t height = 0; |
||||
bool row = false; |
||||
|
||||
each_row_t(MAT &mat) { |
||||
height = matrix::height(mat); |
||||
width = matrix::width(mat); |
||||
} |
||||
|
||||
bool next() { |
||||
x = next_x(x, width); |
||||
y = next_y(x, y); |
||||
row = end_row(x, width); |
||||
return at_end(y, height); |
||||
} |
||||
}; |
||||
|
||||
using each_row = each_row_t<Matrix>; |
||||
|
||||
template<typename MAT> |
||||
struct box_t { |
||||
size_t from_x; |
||||
size_t from_y; |
||||
size_t x = 0; // these are set in constructor
|
||||
size_t y = 0; // again, no fancy ~ trick needed
|
||||
size_t left = 0; |
||||
size_t top = 0; |
||||
size_t right = 0; |
||||
size_t bottom = 0; |
||||
|
||||
box_t(MAT &mat, size_t at_x, size_t at_y, size_t size) : |
||||
from_x(at_x), from_y(at_y) |
||||
{ |
||||
size_t h = matrix::height(mat); |
||||
size_t w = matrix::width(mat); |
||||
|
||||
// keeps it from going below zero
|
||||
// need extra -1 to compensate for the first next()
|
||||
left = max(from_x, size) - size; |
||||
x = left - 1; // must be -1 for next()
|
||||
// keeps it from going above width
|
||||
right = min(from_x + size + 1, w); |
||||
|
||||
// same for these two
|
||||
top = max(from_y, size) - size; |
||||
y = top - (left == 0); |
||||
bottom = min(from_y + size + 1, h); |
||||
} |
||||
|
||||
bool next() { |
||||
// calc next but allow to go to 0 for next
|
||||
x = next_x(x, right); |
||||
// x will go to 0, which signals new line
|
||||
y = next_y(x, y); // this must go here
|
||||
// if x==0 then this moves it to min_x
|
||||
x = max(x, left); |
||||
// and done
|
||||
|
||||
return at_end(y, bottom); |
||||
} |
||||
|
||||
float distance() { |
||||
int dx = from_x - x; |
||||
int dy = from_y - y; |
||||
|
||||
return sqrt((dx * dx) + (dy * dy)); |
||||
} |
||||
}; |
||||
|
||||
using box = box_t<Matrix>; |
||||
|
||||
template<typename MAT> |
||||
struct compass_t { |
||||
size_t x = 0; // these are set in constructor
|
||||
size_t y = 0; // again, no fancy ~ trick needed
|
||||
array<int, 4> x_dirs{0, 1, 0, -1}; |
||||
array<int, 4> y_dirs{-1, 0, 1, 0}; |
||||
size_t max_dirs=0; |
||||
size_t dir = ~0; |
||||
|
||||
compass_t(MAT &mat, size_t x, size_t y) : |
||||
x(x), y(y) |
||||
{ |
||||
array<int, 4> x_in{0, 1, 0, -1}; |
||||
array<int, 4> y_in{-1, 0, 1, 0}; |
||||
|
||||
for(size_t i = 0; i < 4; i++) { |
||||
int nx = x + x_in[i]; |
||||
int ny = y + y_in[i]; |
||||
if(matrix::inbounds(mat, nx, ny)) { |
||||
x_dirs[max_dirs] = nx; |
||||
y_dirs[max_dirs] = ny; |
||||
max_dirs++; |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool next() { |
||||
dir++; |
||||
if(dir < max_dirs) { |
||||
x = x_dirs[dir]; |
||||
y = y_dirs[dir]; |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
using compass = compass_t<Matrix>; |
||||
|
||||
struct flood { |
||||
Matrix &mat; |
||||
Point start; |
||||
int old_val; |
||||
int new_val; |
||||
queue<Point> q; |
||||
Point current_loc; |
||||
int x; |
||||
int y; |
||||
matrix::compass dirs; |
||||
|
||||
flood(Matrix &mat, Point start, int old_val, int new_val); |
||||
bool next(); |
||||
bool next_working(); |
||||
}; |
||||
|
||||
struct line { |
||||
int x; |
||||
int y; |
||||
int x1; |
||||
int y1; |
||||
int sx; |
||||
int sy; |
||||
int dx; |
||||
int dy; |
||||
int error; |
||||
|
||||
line(Point start, Point end); |
||||
bool next(); |
||||
}; |
||||
|
||||
template<typename MAT> |
||||
struct circle_t { |
||||
float center_x; |
||||
float center_y; |
||||
float radius = 0.0f; |
||||
int y = 0; |
||||
int dx = 0; |
||||
int dy = 0; |
||||
int left = 0; |
||||
int right = 0; |
||||
int top = 0; |
||||
int bottom = 0; |
||||
int width = 0; |
||||
int height = 0; |
||||
|
||||
circle_t(MAT &mat, Point center, float radius) : |
||||
center_x(center.x), center_y(center.y), radius(radius) |
||||
{ |
||||
width = matrix::width(mat); |
||||
height = matrix::height(mat); |
||||
top = max(int(floor(center_y - radius)), 0); |
||||
bottom = min(int(floor(center_y + radius)), height - 1); |
||||
|
||||
y = top; |
||||
} |
||||
|
||||
bool next() { |
||||
y++; |
||||
if(y <= bottom) { |
||||
dy = y - center_y; |
||||
dx = floor(sqrt(radius * radius - dy * dy)); |
||||
left = max(0, int(center_x) - dx); |
||||
right = min(width, int(center_x) + dx + 1); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
using circle = circle_t<Matrix>; |
||||
} |
@ -1,27 +1,25 @@ |
||||
project('lodecaster', 'cpp', |
||||
default_options: ['cpp_std=c++20']) |
||||
project('raycaster', 'cpp', |
||||
default_options: ['cpp_std=c++20']) |
||||
|
||||
catch2 = dependency('catch2-with-main') |
||||
fmt = dependency('fmt') |
||||
json = dependency('nlohmann_json') |
||||
sdl2 = dependency('sdl2') |
||||
sdl2_main = dependency('sdl2main') |
||||
sfml = dependency('sfml') |
||||
|
||||
dependencies = [ |
||||
sdl2, sdl2_main, |
||||
fmt, json |
||||
] |
||||
fmt, json, sfml |
||||
] |
||||
|
||||
executable('runtests', [ |
||||
'quickcg.cpp', |
||||
'main.cpp', |
||||
], |
||||
win_subsystem: 'windows', |
||||
dependencies: dependencies) |
||||
'dbc.cpp', |
||||
'matrix.cpp', |
||||
'tests/base.cpp', |
||||
], |
||||
dependencies: dependencies + [catch2]) |
||||
|
||||
executable('lodecaster', [ |
||||
'quickcg.cpp', |
||||
'raycaster_flat.cpp', |
||||
], |
||||
win_subsystem: 'windows', |
||||
dependencies: dependencies) |
||||
executable('raycaster', [ |
||||
'dbc.cpp', |
||||
'matrix.cpp', |
||||
'raycaster.cpp', |
||||
], |
||||
dependencies: dependencies) |
||||
|
@ -0,0 +1,19 @@ |
||||
#pragma once |
||||
#include <vector> |
||||
|
||||
struct Point { |
||||
size_t x = 0; |
||||
size_t y = 0; |
||||
|
||||
bool operator==(const Point& other) const { |
||||
return other.x == x && other.y == y; |
||||
} |
||||
}; |
||||
|
||||
typedef std::vector<Point> PointList; |
||||
|
||||
struct PointHash { |
||||
size_t operator()(const Point& p) const { |
||||
return std::hash<int>()(p.x) ^ std::hash<int>()(p.y); |
||||
} |
||||
}; |
@ -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) |
@ -0,0 +1,102 @@ |
||||
#include <fmt/core.h> |
||||
#include <SFML/Graphics.hpp> |
||||
#include <numbers> |
||||
#include <cmath> |
||||
#include "matrix.hpp" |
||||
|
||||
using matrix::Matrix; |
||||
using namespace fmt; |
||||
|
||||
const int SCREEN_HEIGHT=480; |
||||
const int SCREEN_WIDTH=SCREEN_HEIGHT * 2; |
||||
const int MAP_SIZE=8; |
||||
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; |
||||
|
||||
Matrix MAP{ |
||||
{1,1,1,1,1,1,1,1}, |
||||
{1,0,1,0,0,0,0,1}, |
||||
{1,0,1,0,0,1,1,1}, |
||||
{1,0,0,0,0,0,0,1}, |
||||
{1,1,0,0,0,0,0,1}, |
||||
{1,0,0,1,1,1,0,1}, |
||||
{1,0,0,0,1,0,0,1}, |
||||
{1,1,1,1,1,1,1,1} |
||||
}; |
||||
|
||||
float player_x = SCREEN_WIDTH / 4; |
||||
float player_y = SCREEN_WIDTH / 4; |
||||
float player_angle = std::numbers::pi; |
||||
|
||||
void draw_map_rect(sf::RenderWindow &window, sf::Color color, int x, int y) { |
||||
sf::RectangleShape rect({TILE_SIZE-1, TILE_SIZE-1}); |
||||
rect.setFillColor(color); |
||||
rect.setPosition(x * TILE_SIZE, y * TILE_SIZE); |
||||
window.draw(rect); |
||||
} |
||||
|
||||
void draw_map(sf::RenderWindow &window, Matrix &map) { |
||||
sf::Color light_grey{191, 191, 191}; |
||||
sf::Color dark_grey{65,65,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, map[y][x] == 1 ? light_grey : dark_grey, x, y); |
||||
} |
||||
} |
||||
} |
||||
|
||||
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 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_rect(window, {195, 137, 38}, col & TILE_SIZE, row * TILE_SIZE); |
||||
draw_line(window, {player_x, player_y}, {target_x, target_y}); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
int main() { |
||||
sf::RenderWindow window(sf::VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Raycaster"); |
||||
|
||||
while(window.isOpen()) { |
||||
sf::Event event; |
||||
|
||||
draw_map(window, MAP); |
||||
ray_casting(window, MAP); |
||||
window.display(); |
||||
|
||||
while(window.pollEvent(event)) { |
||||
if(event.type == sf::Event::Closed) { |
||||
window.close(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,471 @@ |
||||
/*
|
||||
Copyright (c) 2004-2020, Lode Vandevenne |
||||
|
||||
All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: |
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#include <cmath> |
||||
#include <string> |
||||
#include <vector> |
||||
#include <iostream> |
||||
|
||||
#include "quickcg.h" |
||||
using namespace QuickCG; |
||||
|
||||
/*
|
||||
g++ *.cpp -lSDL -O3 -W -Wall -ansi -pedantic |
||||
g++ *.cpp -lSDL |
||||
*/ |
||||
|
||||
|
||||
#define screenWidth 640 |
||||
#define screenHeight 480 |
||||
#define texWidth 64 // must be power of two
|
||||
#define texHeight 64 // must be power of two
|
||||
#define mapWidth 24 |
||||
#define mapHeight 24 |
||||
|
||||
int worldMap[mapWidth][mapHeight] = |
||||
{ |
||||
{8,8,8,8,8,8,8,8,8,8,8,4,4,6,4,4,6,4,6,4,4,4,6,4}, |
||||
{8,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,0,0,0,0,0,0,4}, |
||||
{8,0,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,6}, |
||||
{8,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6}, |
||||
{8,0,3,3,0,0,0,0,0,8,8,4,0,0,0,0,0,0,0,0,0,0,0,4}, |
||||
{8,0,0,0,0,0,0,0,0,0,8,4,0,0,0,0,0,6,6,6,0,6,4,6}, |
||||
{8,8,8,8,0,8,8,8,8,8,8,4,4,4,4,4,4,6,0,0,0,0,0,6}, |
||||
{7,7,7,7,0,7,7,7,7,0,8,0,8,0,8,0,8,4,0,4,0,6,0,6}, |
||||
{7,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,0,0,0,0,0,6}, |
||||
{7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,0,0,0,0,4}, |
||||
{7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,0,6,0,6,0,6}, |
||||
{7,7,0,0,0,0,0,0,7,8,0,8,0,8,0,8,8,6,4,6,0,6,6,6}, |
||||
{7,7,7,7,0,7,7,7,7,8,8,4,0,6,8,4,8,3,3,3,0,3,3,3}, |
||||
{2,2,2,2,0,2,2,2,2,4,6,4,0,0,6,0,6,3,0,0,0,0,0,3}, |
||||
{2,2,0,0,0,0,0,2,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,3}, |
||||
{2,0,0,0,0,0,0,0,2,4,0,0,0,0,0,0,4,3,0,0,0,0,0,3}, |
||||
{1,0,0,0,0,0,0,0,1,4,4,4,4,4,6,0,6,3,3,0,0,0,3,3}, |
||||
{2,0,0,0,0,0,0,0,2,2,2,1,2,2,2,6,6,0,0,5,0,5,0,5}, |
||||
{2,2,0,0,0,0,0,2,2,2,0,0,0,2,2,0,5,0,5,0,0,0,5,5}, |
||||
{2,0,0,0,0,0,0,0,2,0,0,0,0,0,2,5,0,5,0,5,0,5,0,5}, |
||||
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5}, |
||||
{2,0,0,0,0,0,0,0,2,0,0,0,0,0,2,5,0,5,0,5,0,5,0,5}, |
||||
{2,2,0,0,0,0,0,2,2,2,0,0,0,2,2,0,5,0,5,0,0,0,5,5}, |
||||
{2,2,2,2,1,2,2,2,2,2,2,1,2,2,2,5,5,5,5,5,5,5,5,5} |
||||
}; |
||||
|
||||
struct Sprite |
||||
{ |
||||
double x; |
||||
double y; |
||||
int texture; |
||||
}; |
||||
|
||||
#define numSprites 19 |
||||
|
||||
Sprite sprite[numSprites] = |
||||
{ |
||||
{20.5, 11.5, 10}, //green light in front of playerstart
|
||||
//green lights in every room
|
||||
{18.5,4.5, 10}, |
||||
{10.0,4.5, 10}, |
||||
{10.0,12.5,10}, |
||||
{3.5, 6.5, 10}, |
||||
{3.5, 20.5,10}, |
||||
{3.5, 14.5,10}, |
||||
{14.5,20.5,10}, |
||||
|
||||
//row of pillars in front of wall: fisheye test
|
||||
{18.5, 10.5, 9}, |
||||
{18.5, 11.5, 9}, |
||||
{18.5, 12.5, 9}, |
||||
|
||||
//some barrels around the map
|
||||
{21.5, 1.5, 8}, |
||||
{15.5, 1.5, 8}, |
||||
{16.0, 1.8, 8}, |
||||
{16.2, 1.2, 8}, |
||||
{3.5, 2.5, 8}, |
||||
{9.5, 15.5, 8}, |
||||
{10.0, 15.1,8}, |
||||
{10.5, 15.8,8}, |
||||
}; |
||||
|
||||
Uint32 buffer[screenHeight][screenWidth]; // y-coordinate first because it works per scanline
|
||||
|
||||
//1D Zbuffer
|
||||
double ZBuffer[screenWidth]; |
||||
|
||||
//arrays used to sort the sprites
|
||||
int spriteOrder[numSprites]; |
||||
double spriteDistance[numSprites]; |
||||
|
||||
//function used to sort the sprites
|
||||
void sortSprites(int* order, double* dist, int amount); |
||||
|
||||
int main(int /*argc*/, char */*argv*/[]) |
||||
{ |
||||
double posX = 22.0, posY = 11.5; //x and y start position
|
||||
double dirX = -1.0, dirY = 0.0; //initial direction vector
|
||||
double planeX = 0.0, planeY = 0.66; //the 2d raycaster version of camera plane
|
||||
|
||||
double time = 0; //time of current frame
|
||||
double oldTime = 0; //time of previous frame
|
||||
|
||||
std::vector<Uint32> texture[11]; |
||||
for(int i = 0; i < 11; i++) texture[i].resize(texWidth * texHeight); |
||||
|
||||
screen(screenWidth,screenHeight, 0, "Raycaster"); |
||||
|
||||
//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"); |
||||
if(error) { std::cout << "error loading images" << std::endl; return 1; } |
||||
|
||||
//load some sprite textures
|
||||
error |= loadImage(texture[8], tw, th, "pics/barrel.png"); |
||||
error |= loadImage(texture[9], tw, th, "pics/pillar.png"); |
||||
error |= loadImage(texture[10], tw, th, "pics/greenlight.png"); |
||||
if(error) { std::cout << "error loading images" << std::endl; return 1; } |
||||
|
||||
//start the main loop
|
||||
while(!done()) |
||||
{ |
||||
//FLOOR CASTING
|
||||
for(int y = screenHeight / 2 + 1; y < screenHeight; ++y) |
||||
{ |
||||
// rayDir for leftmost ray (x = 0) and rightmost ray (x = w)
|
||||
float rayDirX0 = dirX - planeX; |
||||
float rayDirY0 = dirY - planeY; |
||||
float rayDirX1 = dirX + planeX; |
||||
float rayDirY1 = dirY + planeY; |
||||
|
||||
// Current y position compared to the center of the screen (the horizon)
|
||||
int p = y - screenHeight / 2; |
||||
|
||||
// Vertical position of the camera.
|
||||
float posZ = 0.5 * screenHeight; |
||||
|
||||
// Horizontal distance from the camera to the floor for the current row.
|
||||
// 0.5 is the z position exactly in the middle between floor and ceiling.
|
||||
float rowDistance = posZ / p; |
||||
|
||||
// calculate the real world step vector we have to add for each x (parallel to camera plane)
|
||||
// adding step by step avoids multiplications with a weight in the inner loop
|
||||
float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / screenWidth; |
||||
float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / screenWidth; |
||||
|
||||
// real world coordinates of the leftmost column. This will be updated as we step to the right.
|
||||
float floorX = posX + rowDistance * rayDirX0; |
||||
float floorY = posY + rowDistance * rayDirY0; |
||||
|
||||
for(int x = 0; x < screenWidth; ++x) |
||||
{ |
||||
// the cell coord is simply got from the integer parts of floorX and floorY
|
||||
int cellX = (int)(floorX); |
||||
int cellY = (int)(floorY); |
||||
|
||||
// get the texture coordinate from the fractional part
|
||||
int tx = (int)(texWidth * (floorX - cellX)) & (texWidth - 1); |
||||
int ty = (int)(texHeight * (floorY - cellY)) & (texHeight - 1); |
||||
|
||||
floorX += floorStepX; |
||||
floorY += floorStepY; |
||||
|
||||
// choose texture and draw the pixel
|
||||
int checkerBoardPattern = (int(cellX + cellY)) & 1; |
||||
int floorTexture; |
||||
if(checkerBoardPattern == 0) floorTexture = 3; |
||||
else floorTexture = 4; |
||||
int ceilingTexture = 6; |
||||
Uint32 color; |
||||
|
||||
// floor
|
||||
color = texture[floorTexture][texWidth * ty + tx]; |
||||
color = (color >> 1) & 8355711; // make a bit darker
|
||||
buffer[y][x] = color; |
||||
|
||||
//ceiling (symmetrical, at screenHeight - y - 1 instead of y)
|
||||
color = texture[ceilingTexture][texWidth * ty + tx]; |
||||
color = (color >> 1) & 8355711; // make a bit darker
|
||||
buffer[screenHeight - y - 1][x] = color; |
||||
} |
||||
} |
||||
|
||||
// WALL CASTING
|
||||
for(int x = 0; x < w; x++) |
||||
{ |
||||
//calculate ray position and direction
|
||||
double cameraX = 2 * x / double(w) - 1; //x-coordinate 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 position 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 = (rayDirX == 0) ? 1e30 : std::abs(1 / rayDirX); |
||||
double deltaDistY = (rayDirY == 0) ? 1e30 : std::abs(1 / rayDirY); |
||||
double perpWallDist; |
||||
|
||||
//what direction to step in x or y-direction (either +1 or -1)
|
||||
int stepX; |
||||
int stepY; |
||||
|
||||
int hit = 0; //was there a wall hit?
|
||||
int side; //was a NS or a EW wall hit?
|
||||
|
||||
//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) |
||||
{ |
||||
//jump to next map square, either in x-direction, or in y-direction
|
||||
if(sideDistX < sideDistY) |
||||
{ |
||||
sideDistX += deltaDistX; |
||||
mapX += stepX; |
||||
side = 0; |
||||
} |
||||
else |
||||
{ |
||||
sideDistY += deltaDistY; |
||||
mapY += stepY; |
||||
side = 1; |
||||
} |
||||
//Check if ray has hit a wall
|
||||
if(worldMap[mapX][mapY] > 0) hit = 1; |
||||
} |
||||
|
||||
//Calculate distance of perpendicular ray (Euclidean distance would give fisheye effect!)
|
||||
if(side == 0) perpWallDist = (sideDistX - deltaDistX); |
||||
else perpWallDist = (sideDistY - deltaDistY); |
||||
|
||||
//Calculate height of line to draw on screen
|
||||
int lineHeight = (int)(h / perpWallDist); |
||||
|
||||
//calculate lowest and highest pixel to fill in current stripe
|
||||
int drawStart = -lineHeight / 2 + h / 2; |
||||
if(drawStart < 0) drawStart = 0; |
||||
int drawEnd = lineHeight / 2 + h / 2; |
||||
if(drawEnd >= h) drawEnd = h - 1; |
||||
//texturing calculations
|
||||
int texNum = worldMap[mapX][mapY] - 1; //1 subtracted from it so that texture 0 can be used!
|
||||
|
||||
//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 coordinate 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; |
||||
|
||||
// TODO: 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 - h / 2 + lineHeight / 2) * step; |
||||
for(int y = drawStart; y < drawEnd; y++) |
||||
{ |
||||
// Cast the texture coordinate to integer, and mask with (texHeight - 1) in case of overflow
|
||||
int texY = (int)texPos & (texHeight - 1); |
||||
texPos += step; |
||||
Uint32 color = texture[texNum][texHeight * texY + texX]; |
||||
//make color darker for y-sides: R, G and B byte each divided through two with a "shift" and an "and"
|
||||
if(side == 1) color = (color >> 1) & 8355711; |
||||
buffer[y][x] = color; |
||||
} |
||||
|
||||
//SET THE ZBUFFER FOR THE SPRITE CASTING
|
||||
ZBuffer[x] = perpWallDist; //perpendicular distance is used
|
||||
} |
||||
|
||||
//SPRITE CASTING
|
||||
//sort sprites from far to close
|
||||
for(int i = 0; i < numSprites; i++) |
||||
{ |
||||
spriteOrder[i] = i; |
||||
spriteDistance[i] = ((posX - sprite[i].x) * (posX - sprite[i].x) + (posY - sprite[i].y) * (posY - sprite[i].y)); //sqrt not taken, unneeded
|
||||
} |
||||
sortSprites(spriteOrder, spriteDistance, numSprites); |
||||
|
||||
//after sorting the sprites, do the projection and draw them
|
||||
for(int i = 0; i < numSprites; i++) |
||||
{ |
||||
//translate sprite position to relative to camera
|
||||
double spriteX = sprite[spriteOrder[i]].x - posX; |
||||
double spriteY = sprite[spriteOrder[i]].y - posY; |
||||
|
||||
//transform sprite with the inverse camera matrix
|
||||
// [ planeX dirX ] -1 [ dirY -dirX ]
|
||||
// [ ] = 1/(planeX*dirY-dirX*planeY) * [ ]
|
||||
// [ planeY dirY ] [ -planeY planeX ]
|
||||
|
||||
double invDet = 1.0 / (planeX * dirY - dirX * planeY); //required for correct matrix multiplication
|
||||
|
||||
double transformX = invDet * (dirY * spriteX - dirX * spriteY); |
||||
double transformY = invDet * (-planeY * spriteX + planeX * spriteY); //this is actually the depth inside the screen, that what Z is in 3D, the distance of sprite to player, matching sqrt(spriteDistance[i])
|
||||
|
||||
int spriteScreenX = int((w / 2) * (1 + transformX / transformY)); |
||||
|
||||
//parameters for scaling and moving the sprites
|
||||
#define uDiv 1 |
||||
#define vDiv 1 |
||||
#define vMove 0.0 |
||||
int vMoveScreen = int(vMove / transformY); |
||||
|
||||
//calculate height of the sprite on screen
|
||||
int spriteHeight = abs(int(h / (transformY))) / vDiv; //using "transformY" instead of the real distance prevents fisheye
|
||||
//calculate lowest and highest pixel to fill in current stripe
|
||||
int drawStartY = -spriteHeight / 2 + h / 2 + vMoveScreen; |
||||
if(drawStartY < 0) drawStartY = 0; |
||||
int drawEndY = spriteHeight / 2 + h / 2 + vMoveScreen; |
||||
if(drawEndY >= h) drawEndY = h - 1; |
||||
|
||||
//calculate width of the sprite
|
||||
int spriteWidth = abs(int (h / (transformY))) / uDiv; // same as height of sprite, given that it's square
|
||||
int drawStartX = -spriteWidth / 2 + spriteScreenX; |
||||
if(drawStartX < 0) drawStartX = 0; |
||||
int drawEndX = spriteWidth / 2 + spriteScreenX; |
||||
if(drawEndX > w) drawEndX = w; |
||||
|
||||
//loop through every vertical stripe of the sprite on screen
|
||||
for(int stripe = drawStartX; stripe < drawEndX; stripe++) |
||||
{ |
||||
int texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texWidth / spriteWidth) / 256; |
||||
//the conditions in the if are:
|
||||
//1) it's in front of camera plane so you don't see things behind you
|
||||
//2) ZBuffer, with perpendicular distance
|
||||
if(transformY > 0 && transformY < ZBuffer[stripe]) |
||||
{ |
||||
for(int y = drawStartY; y < drawEndY; y++) //for every pixel of the current stripe
|
||||
{ |
||||
int d = (y - vMoveScreen) * 256 - h * 128 + spriteHeight * 128; //256 and 128 factors to avoid floats
|
||||
int texY = ((d * texHeight) / spriteHeight) / 256; |
||||
Uint32 color = texture[sprite[spriteOrder[i]].texture][texWidth * texY + texX]; //get current color from the texture
|
||||
if((color & 0x00FFFFFF) != 0) buffer[y][stripe] = color; //paint pixel if it isn't black, black is the invisible color
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
drawBuffer(buffer[0]); |
||||
// No need to clear the screen here, since everything is overdrawn with floor and ceiling
|
||||
|
||||
//timing for input and FPS counter
|
||||
oldTime = time; |
||||
time = getTicks(); |
||||
double frameTime = (time - oldTime) / 1000.0; //frametime is the time this frame has taken, in seconds
|
||||
print(1.0 / frameTime); //FPS counter
|
||||
redraw(); |
||||
|
||||
//speed modifiers
|
||||
double moveSpeed = frameTime * 3.0; //the constant value is in squares/second
|
||||
double rotSpeed = frameTime * 2.0; //the constant value is in radians/second
|
||||
|
||||
SDL_Event event; |
||||
while(SDL_PollEvent(&event)) { |
||||
if(event.type != SDL_KEYDOWN) continue; |
||||
//move forward if no wall in front of you
|
||||
if(event.key.keysym.sym == SDLK_UP) |
||||
{ |
||||
if(worldMap[int(posX + dirX * moveSpeed)][int(posY)] == false) posX += dirX * moveSpeed; |
||||
if(worldMap[int(posX)][int(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed; |
||||
} |
||||
//move backwards if no wall behind you
|
||||
if(event.key.keysym.sym == SDLK_DOWN) |
||||
{ |
||||
if(worldMap[int(posX - dirX * moveSpeed)][int(posY)] == false) posX -= dirX * moveSpeed; |
||||
if(worldMap[int(posX)][int(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed; |
||||
} |
||||
//rotate to the right
|
||||
if(event.key.keysym.sym == SDLK_RIGHT) |
||||
{ |
||||
//both camera direction and camera plane must be rotated
|
||||
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); |
||||
} |
||||
//rotate to the left
|
||||
if(event.key.keysym.sym == SDLK_LEFT) |
||||
{ |
||||
//both camera direction and camera plane must be rotated
|
||||
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); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
//sort the sprites based on distance
|
||||
void sortSprites(int* order, double* dist, int amount) |
||||
{ |
||||
std::vector<std::pair<double, int>> sprites(amount); |
||||
for(int i = 0; i < amount; i++) { |
||||
sprites[i].first = dist[i]; |
||||
sprites[i].second = order[i]; |
||||
} |
||||
std::sort(sprites.begin(), sprites.end()); |
||||
// restore in reverse order to go from farthest to nearest
|
||||
for(int i = 0; i < amount; i++) { |
||||
dist[i] = sprites[amount - i - 1].first; |
||||
order[i] = sprites[amount - i - 1].second; |
||||
} |
||||
} |
@ -0,0 +1,293 @@ |
||||
/*
|
||||
Copyright (c) 2004-2019, Lode Vandevenne |
||||
|
||||
All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: |
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
#include <cmath> |
||||
#include <string> |
||||
#include <vector> |
||||
#include <iostream> |
||||
|
||||
#include "quickcg.h" |
||||
using namespace QuickCG; |
||||
|
||||
/*
|
||||
g++ *.cpp -lSDL -O3 -W -Wall -ansi -pedantic |
||||
g++ *.cpp -lSDL |
||||
*/ |
||||
|
||||
|
||||
#define screenWidth 640 |
||||
#define screenHeight 480 |
||||
#define texWidth 64 |
||||
#define texHeight 64 |
||||
#define mapWidth 24 |
||||
#define mapHeight 24 |
||||
|
||||
int worldMap[mapWidth][mapHeight]= |
||||
{ |
||||
{4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,7,7,7,7,7,7,7,7}, |
||||
{4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,7}, |
||||
{4,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7}, |
||||
{4,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7}, |
||||
{4,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,7}, |
||||
{4,0,4,0,0,0,0,5,5,5,5,5,5,5,5,5,7,7,0,7,7,7,7,7}, |
||||
{4,0,5,0,0,0,0,5,0,5,0,5,0,5,0,5,7,0,0,0,7,7,7,1}, |
||||
{4,0,6,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,0,0,0,8}, |
||||
{4,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,1}, |
||||
{4,0,8,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,0,0,0,8}, |
||||
{4,0,0,0,0,0,0,5,0,0,0,0,0,0,0,5,7,0,0,0,7,7,7,1}, |
||||
{4,0,0,0,0,0,0,5,5,5,5,0,5,5,5,5,7,7,7,7,7,7,7,1}, |
||||
{6,6,6,6,6,6,6,6,6,6,6,0,6,6,6,6,6,6,6,6,6,6,6,6}, |
||||
{8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4}, |
||||
{6,6,6,6,6,6,0,6,6,6,6,0,6,6,6,6,6,6,6,6,6,6,6,6}, |
||||
{4,4,4,4,4,4,0,4,4,4,6,0,6,2,2,2,2,2,2,2,3,3,3,3}, |
||||
{4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,0,0,0,2}, |
||||
{4,0,0,0,0,0,0,0,0,0,0,0,6,2,0,0,5,0,0,2,0,0,0,2}, |
||||
{4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,2,0,2,2}, |
||||
{4,0,6,0,6,0,0,0,0,4,6,0,0,0,0,0,5,0,0,0,0,0,0,2}, |
||||
{4,0,0,5,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,2,0,2,2}, |
||||
{4,0,6,0,6,0,0,0,0,4,6,0,6,2,0,0,5,0,0,2,0,0,0,2}, |
||||
{4,0,0,0,0,0,0,0,0,4,6,0,6,2,0,0,0,0,0,2,0,0,0,2}, |
||||
{4,4,4,4,4,4,4,4,4,4,1,1,1,2,2,2,2,2,2,3,3,3,3,3} |
||||
}; |
||||
|
||||
Uint32 buffer[screenHeight][screenWidth]; |
||||
|
||||
int main(int /*argc*/, char */*argv*/[]) |
||||
{ |
||||
double posX = 22.0, posY = 11.5; //x and y start position
|
||||
double dirX = -1.0, dirY = 0.0; //initial direction vector
|
||||
double planeX = 0.0, planeY = 0.66; //the 2d raycaster version of camera plane
|
||||
|
||||
double time = 0; //time of current frame
|
||||
double oldTime = 0; //time of previous frame
|
||||
|
||||
std::vector<Uint32> texture[8]; |
||||
for(int i = 0; i < 8; i++) texture[i].resize(texWidth * texHeight); |
||||
|
||||
screen(screenWidth,screenHeight, 0, "Raycaster"); |
||||
|
||||
//generate some textures
|
||||
#if 0 |
||||
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
|
||||
} |
||||
#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"); |
||||
#endif |
||||
|
||||
//start the main loop
|
||||
while(!done()) |
||||
{ |
||||
for(int x = 0; x < w; x++) |
||||
{ |
||||
//calculate ray position and direction
|
||||
double cameraX = 2 * x / (double)w - 1; //x-coordinate 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 position 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 = (rayDirX == 0) ? 1e30 : std::abs(1 / rayDirX); |
||||
double deltaDistY = (rayDirY == 0) ? 1e30 : std::abs(1 / rayDirY); |
||||
double perpWallDist; |
||||
|
||||
//what direction to step in x or y-direction (either +1 or -1)
|
||||
int stepX; |
||||
int stepY; |
||||
|
||||
int hit = 0; //was there a wall hit?
|
||||
int side; //was a NS or a EW wall hit?
|
||||
|
||||
//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) |
||||
{ |
||||
//jump to next map square, either in x-direction, or in y-direction
|
||||
if(sideDistX < sideDistY) |
||||
{ |
||||
sideDistX += deltaDistX; |
||||
mapX += stepX; |
||||
side = 0; |
||||
} |
||||
else |
||||
{ |
||||
sideDistY += deltaDistY; |
||||
mapY += stepY; |
||||
side = 1; |
||||
} |
||||
//Check if ray has hit a wall
|
||||
if(worldMap[mapX][mapY] > 0) hit = 1; |
||||
} |
||||
|
||||
//Calculate distance of perpendicular ray (Euclidean distance would give fisheye effect!)
|
||||
if(side == 0) perpWallDist = (sideDistX - deltaDistX); |
||||
else perpWallDist = (sideDistY - deltaDistY); |
||||
|
||||
//Calculate height of line to draw on screen
|
||||
int lineHeight = (int)(h / perpWallDist); |
||||
|
||||
|
||||
int pitch = 100; |
||||
|
||||
//calculate lowest and highest pixel to fill in current stripe
|
||||
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; |
||||
|
||||
//texturing calculations
|
||||
int texNum = worldMap[mapX][mapY] - 1; //1 subtracted from it so that texture 0 can be used!
|
||||
|
||||
//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 coordinate 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; |
||||
|
||||
// TODO: 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++) |
||||
{ |
||||
// Cast the texture coordinate to integer, and mask with (texHeight - 1) in case of overflow
|
||||
int texY = (int)texPos & (texHeight - 1); |
||||
texPos += step; |
||||
Uint32 color = texture[texNum][texHeight * texY + texX]; |
||||
//make color darker for y-sides: R, G and B byte each divided through two with a "shift" and an "and"
|
||||
if(side == 1) color = (color >> 1) & 8355711; |
||||
buffer[y][x] = color; |
||||
} |
||||
} |
||||
|
||||
for(int y = 0; y < h; y++) for(int x = 0; x < w; x++) buffer[y][x] = 0; //clear the buffer instead of cls()
|
||||
drawBuffer(buffer[0]); |
||||
|
||||
//timing for input and FPS counter
|
||||
oldTime = time; |
||||
time = getTicks(); |
||||
double frameTime = (time - oldTime) / 1000.0; //frametime is the time this frame has taken, in seconds
|
||||
print(1.0 / frameTime); //FPS counter
|
||||
redraw(); |
||||
|
||||
//speed modifiers
|
||||
double moveSpeed = frameTime * 5.0; //the constant value is in squares/second
|
||||
double rotSpeed = frameTime * 3.0; //the constant value is in radians/second
|
||||
|
||||
SDL_Event event; |
||||
while(SDL_PollEvent(&event)) { |
||||
if(event.type != SDL_KEYDOWN) continue; |
||||
//move forward if no wall in front of you
|
||||
if(event.key.keysym.sym == SDLK_UP) |
||||
{ |
||||
if(worldMap[int(posX + dirX * moveSpeed)][int(posY)] == false) posX += dirX * moveSpeed; |
||||
if(worldMap[int(posX)][int(posY + dirY * moveSpeed)] == false) posY += dirY * moveSpeed; |
||||
} |
||||
//move backwards if no wall behind you
|
||||
if(event.key.keysym.sym == SDLK_DOWN) |
||||
{ |
||||
if(worldMap[int(posX - dirX * moveSpeed)][int(posY)] == false) posX -= dirX * moveSpeed; |
||||
if(worldMap[int(posX)][int(posY - dirY * moveSpeed)] == false) posY -= dirY * moveSpeed; |
||||
} |
||||
//rotate to the right
|
||||
if(event.key.keysym.sym == SDLK_RIGHT) |
||||
{ |
||||
//both camera direction and camera plane must be rotated
|
||||
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); |
||||
} |
||||
//rotate to the left
|
||||
if(event.key.keysym.sym == SDLK_LEFT) |
||||
{ |
||||
//both camera direction and camera plane must be rotated
|
||||
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); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,553 @@ |
||||
#include <math.h> |
||||
#include <stdio.h> |
||||
#include <stdint.h> |
||||
#include <sys/time.h> |
||||
#include <SDL.h> |
||||
|
||||
#define ASSERT(_e, ...) if (!(_e)) { fprintf(stderr, __VA_ARGS__); exit(1); } |
||||
|
||||
typedef float f32; |
||||
typedef double f64; |
||||
typedef uint8_t u8; |
||||
typedef uint16_t u16; |
||||
typedef uint32_t u32; |
||||
typedef uint64_t u64; |
||||
typedef int8_t i8; |
||||
typedef int16_t i16; |
||||
typedef int32_t i32; |
||||
typedef int64_t i64; |
||||
typedef size_t usize; |
||||
typedef ssize_t isize; |
||||
|
||||
#define SCREEN_SIZE_X 640 |
||||
#define SCREEN_SIZE_Y 360 |
||||
|
||||
#define TILE_WIDTH 1.0f |
||||
#define WALL_HEIGHT 1.2f |
||||
|
||||
typedef struct v2_s {f32 x, y;} v2; |
||||
typedef struct v2i_s { i32 x, y;} v2i; |
||||
|
||||
#define dot(v0, v1) \ |
||||
({ const v2 _v0 = (v0), _v1 = (v1); (_v0.x * _v1.x) + (_v0.y * _v1.y); }) |
||||
#define length(v) ({ const v2 _v = (v); sqrtf(dot(_v, _v)); }) |
||||
#define normalize(u) ({ \ |
||||
const v2 _u = (u); \
|
||||
const f32 l = length(_u); \
|
||||
(v2) { _u.x/l, _u.y/l }; \
|
||||
}) |
||||
#define rotr(v) ({ const v2 _v = (v); (v2) { -_v.y, _v.x }; }) |
||||
#define min(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a < _b ? _a : _b; }) |
||||
#define max(a, b) ({ __typeof__(a) _a = (a), _b = (b); _a > _b ? _a : _b; }) |
||||
|
||||
static u8 MAPDATA[8*13] = { |
||||
1, 1, 1, 1, 1, 1, 1, 1, |
||||
1, 0, 0, 0, 0, 0, 0, 1, |
||||
1, 0, 0, 3, 0, 0, 4, 1, |
||||
1, 0, 0, 0, 0, 0, 0, 1, |
||||
1, 0, 0, 0, 0, 0, 4, 1, |
||||
1, 0, 2, 0, 0, 0, 0, 1, |
||||
1, 0, 0, 0, 0, 0, 0, 1, |
||||
1, 0, 0, 0, 0, 0, 0, 1, |
||||
1, 0, 2, 2, 0, 0, 0, 1, |
||||
1, 0, 2, 2, 0, 3, 0, 1, |
||||
1, 0, 0, 0, 0, 3, 0, 1, |
||||
1, 0, 0, 0, 0, 0, 0, 1, |
||||
1, 1, 1, 1, 1, 1, 1, 1, |
||||
}; |
||||
|
||||
enum KeyboardKeyState { |
||||
KeyboardKeyState_Depressed = 0, // No recent event, key is still up
|
||||
KeyboardKeyState_Released = 1, // Last event was a released event
|
||||
KeyboardKeyState_Held = 2, // No recent event, key is still down
|
||||
KeyboardKeyState_Pressed = 3, // Last event was a pressed event
|
||||
KeyboardKeyState_COUNT = 4 |
||||
}; |
||||
|
||||
struct KeyBoardState { |
||||
enum KeyboardKeyState up; |
||||
enum KeyboardKeyState down; |
||||
enum KeyboardKeyState right; |
||||
enum KeyboardKeyState left; |
||||
enum KeyboardKeyState a; |
||||
enum KeyboardKeyState s; |
||||
enum KeyboardKeyState d; |
||||
enum KeyboardKeyState w; |
||||
enum KeyboardKeyState q; |
||||
enum KeyboardKeyState e; |
||||
|
||||
enum KeyboardKeyState one; |
||||
enum KeyboardKeyState two; |
||||
enum KeyboardKeyState three; |
||||
enum KeyboardKeyState four; |
||||
enum KeyboardKeyState five; |
||||
enum KeyboardKeyState six; |
||||
enum KeyboardKeyState seven; |
||||
enum KeyboardKeyState eight; |
||||
}; |
||||
|
||||
void clear_keyboard_state(struct KeyBoardState* kbs) { |
||||
kbs->up = KeyboardKeyState_Depressed; |
||||
kbs->down = KeyboardKeyState_Depressed; |
||||
kbs->right = KeyboardKeyState_Depressed; |
||||
kbs->left = KeyboardKeyState_Depressed; |
||||
kbs->a = KeyboardKeyState_Depressed; |
||||
kbs->s = KeyboardKeyState_Depressed; |
||||
kbs->d = KeyboardKeyState_Depressed; |
||||
kbs->w = KeyboardKeyState_Depressed; |
||||
kbs->q = KeyboardKeyState_Depressed; |
||||
kbs->e = KeyboardKeyState_Depressed; |
||||
|
||||
kbs->one = KeyboardKeyState_Depressed; |
||||
kbs->two = KeyboardKeyState_Depressed; |
||||
kbs->three = KeyboardKeyState_Depressed; |
||||
kbs->four = KeyboardKeyState_Depressed; |
||||
kbs->five = KeyboardKeyState_Depressed; |
||||
kbs->six = KeyboardKeyState_Depressed; |
||||
kbs->seven = KeyboardKeyState_Depressed; |
||||
kbs->eight = KeyboardKeyState_Depressed; |
||||
} |
||||
|
||||
void decay_keyboard_state(struct KeyBoardState* kbs) { |
||||
static enum KeyboardKeyState to_depressed_state[KeyboardKeyState_COUNT] = { |
||||
KeyboardKeyState_Depressed, |
||||
KeyboardKeyState_Depressed, |
||||
KeyboardKeyState_Held, |
||||
KeyboardKeyState_Held |
||||
}; |
||||
|
||||
kbs->up = to_depressed_state[kbs->up]; |
||||
kbs->down = to_depressed_state[kbs->down]; |
||||
kbs->right = to_depressed_state[kbs->right]; |
||||
kbs->left = to_depressed_state[kbs->left]; |
||||
kbs->a = to_depressed_state[kbs->a]; |
||||
kbs->s = to_depressed_state[kbs->s]; |
||||
kbs->d = to_depressed_state[kbs->d]; |
||||
kbs->w = to_depressed_state[kbs->w]; |
||||
kbs->q = to_depressed_state[kbs->q]; |
||||
kbs->e = to_depressed_state[kbs->e]; |
||||
|
||||
kbs->one = to_depressed_state[kbs->one]; |
||||
kbs->two = to_depressed_state[kbs->two]; |
||||
kbs->three = to_depressed_state[kbs->three]; |
||||
kbs->four = to_depressed_state[kbs->four]; |
||||
kbs->five = to_depressed_state[kbs->five]; |
||||
kbs->six = to_depressed_state[kbs->six]; |
||||
kbs->seven = to_depressed_state[kbs->seven]; |
||||
kbs->eight = to_depressed_state[kbs->eight]; |
||||
} |
||||
|
||||
bool is_pressed(enum KeyboardKeyState state) { |
||||
static bool lookup[KeyboardKeyState_COUNT] = {0, 0, 1, 1}; |
||||
return lookup[state]; |
||||
} |
||||
|
||||
// TODO: Could we store the pixels in column-major? We're always rendering
|
||||
// in vertical lines, so I suspect that would be more efficient.
|
||||
struct { |
||||
SDL_Window *window; |
||||
SDL_Texture *texture; |
||||
SDL_Renderer *renderer; |
||||
u32 pixels[SCREEN_SIZE_X * SCREEN_SIZE_Y]; |
||||
bool quit; |
||||
|
||||
v2 camera_pos; |
||||
v2 camera_dir; |
||||
v2 camera_dir_rotr; |
||||
f32 camera_width; |
||||
f32 camera_height; |
||||
f32 camera_z; |
||||
|
||||
v2 player_speed; |
||||
f32 player_omega; |
||||
|
||||
struct KeyBoardState keyboard_state; |
||||
} state; |
||||
|
||||
|
||||
static void tick(f32 dt) { |
||||
|
||||
v2 input_dir = {0.0, 0.0}; // In the body frame, which is right-handed, so y points left.
|
||||
if (is_pressed(state.keyboard_state.w)) { |
||||
input_dir.x += 1.0; |
||||
} |
||||
if (is_pressed(state.keyboard_state.s)) { |
||||
input_dir.x -= 1.0; |
||||
} |
||||
if (is_pressed(state.keyboard_state.d)) { |
||||
input_dir.y -= 1.0; |
||||
} |
||||
if (is_pressed(state.keyboard_state.a)) { |
||||
input_dir.y += 1.0; |
||||
} |
||||
|
||||
int input_rot_dir = 0; // Right-hand rotation in plane (CCW)
|
||||
if (is_pressed(state.keyboard_state.q)) { |
||||
input_rot_dir += 1; |
||||
} |
||||
if (is_pressed(state.keyboard_state.e)) { |
||||
input_rot_dir -= 1; |
||||
} |
||||
|
||||
if (is_pressed(state.keyboard_state.three)) { |
||||
state.camera_z *= 0.95; |
||||
printf("camera z: %.3f\n", state.camera_z); |
||||
} |
||||
if (is_pressed(state.keyboard_state.four)) { |
||||
state.camera_z /= 0.95; |
||||
printf("camera z: %.3f\n", state.camera_z); |
||||
} |
||||
if (is_pressed(state.keyboard_state.five)) { |
||||
state.camera_height *= 0.95; |
||||
printf("camera height: %.3f\n", state.camera_height); |
||||
} |
||||
if (is_pressed(state.keyboard_state.six)) { |
||||
state.camera_height /= 0.95; |
||||
printf("camera height: %.3f\n", state.camera_height); |
||||
} |
||||
if (is_pressed(state.keyboard_state.seven)) { |
||||
state.camera_width *= 0.95; |
||||
printf("camera width: %.3f\n", state.camera_width); |
||||
} |
||||
if (is_pressed(state.keyboard_state.eight)) { |
||||
state.camera_width /= 0.95; |
||||
printf("camera width: %.3f\n", state.camera_width); |
||||
} |
||||
|
||||
// Update the player's velocity
|
||||
const f32 kPlayerInputAccel = 7.5; |
||||
const f32 kPlayerInputAngularAccel = 9.5; |
||||
const f32 kPlayerMaxSpeed = 7.0; |
||||
const f32 kPlayerMaxOmega = 7.0; |
||||
const f32 kAirFriction = 0.9; |
||||
const f32 kAirFrictionRot = 0.85; |
||||
|
||||
// Note: Speed is in the global frame
|
||||
state.player_speed.x += (state.camera_dir.x*input_dir.x + state.camera_dir_rotr.x*input_dir.y) * kPlayerInputAccel * dt; |
||||
state.player_speed.y += (state.camera_dir.y*input_dir.x + state.camera_dir_rotr.y*input_dir.y) * kPlayerInputAccel * dt; |
||||
state.player_omega += input_rot_dir * kPlayerInputAngularAccel * dt; |
||||
|
||||
// Clamp the velocity to a maximum magnitude
|
||||
f32 speed = length(state.player_speed); |
||||
if (speed > kPlayerMaxSpeed) { |
||||
state.player_speed.x *= kPlayerMaxSpeed / speed; |
||||
state.player_speed.y *= kPlayerMaxSpeed / speed; |
||||
} |
||||
if (state.player_omega > kPlayerMaxOmega) { |
||||
state.player_omega *= kPlayerMaxOmega / state.player_omega; |
||||
} else if (state.player_omega < -kPlayerMaxOmega) { |
||||
state.player_omega *= - kPlayerMaxOmega / state.player_omega; |
||||
} |
||||
|
||||
// Update the player's position
|
||||
state.camera_pos.x += state.player_speed.x * dt; |
||||
state.camera_pos.y += state.player_speed.y * dt; |
||||
|
||||
// Update the player's rotational heading
|
||||
float theta = atan2(state.camera_dir.y, state.camera_dir.x); |
||||
theta += state.player_omega * dt; |
||||
state.camera_dir = ((v2) {cos(theta), sin(theta)}); |
||||
state.camera_dir_rotr = rotr((state.camera_dir)); |
||||
|
||||
// Apply air friction
|
||||
state.player_speed.x *= kAirFriction; |
||||
state.player_speed.y *= kAirFriction; |
||||
state.player_omega *= kAirFrictionRot; |
||||
} |
||||
|
||||
|
||||
// Fill all pixels in the vertical line at x between y0 and y1 with the given color.
|
||||
static void draw_column(int x, int y0, int y1, u32 color) { |
||||
for (int y = y0; y <= y1; y++) { |
||||
state.pixels[(y * SCREEN_SIZE_X) + x] = color; |
||||
} |
||||
} |
||||
|
||||
static void render() { |
||||
static u32 color_wall[4] = { |
||||
0xFFFF0000, |
||||
0xFF00FF00, |
||||
0xFF00FFFF, |
||||
0xFF0000FF |
||||
}; |
||||
static u32 color_wall_light[4] = { |
||||
0xFFFF3333, |
||||
0xFF66FF66, |
||||
0xFF88FFFF, |
||||
0xFF3333FF |
||||
}; |
||||
const u32 color_floor = 0xFF666666; |
||||
const u32 color_ceil = 0xFF444444; |
||||
|
||||
// Get camera location's cell coordinates
|
||||
int x_ind_cam = (int)(floorf(state.camera_pos.x / TILE_WIDTH)); |
||||
int y_ind_cam = (int)(floorf(state.camera_pos.y / TILE_WIDTH)); |
||||
f32 x_rem_cam = state.camera_pos.x - TILE_WIDTH*x_ind_cam; |
||||
f32 y_rem_cam = state.camera_pos.y - TILE_WIDTH*y_ind_cam; |
||||
|
||||
for (int x = 0; x < SCREEN_SIZE_X; x++) { |
||||
|
||||
// Camera to pixel column
|
||||
const f32 dw = state.camera_width/2 - (state.camera_width*x)/SCREEN_SIZE_X; |
||||
const v2 cp = { |
||||
state.camera_dir.x + dw*state.camera_dir_rotr.x, |
||||
state.camera_dir.y + dw*state.camera_dir_rotr.y |
||||
}; |
||||
|
||||
// Distance from the camera to the column
|
||||
const f32 cam_len = length( (cp) ); |
||||
|
||||
// Ray direction through this column
|
||||
const v2 dir = {cp.x / cam_len, cp.y /cam_len}; |
||||
|
||||
// Start at the camera pos
|
||||
int x_ind = x_ind_cam; |
||||
int y_ind = y_ind_cam; |
||||
f32 x_rem = x_rem_cam; |
||||
f32 y_rem = y_rem_cam; |
||||
|
||||
// We will be raycasting through cells of unit width.
|
||||
// Our ray's position vs time is:
|
||||
// x(t) = x_rem + dir.x * dt
|
||||
// y(t) = y_rem + dir.y * dt
|
||||
|
||||
// We cross x = 0 if dir.x < 0, at dt = -x_rem/dir.x
|
||||
// We cross x = TILE_WIDTH if dir.x > 0, at dt = (TILE_WIDTH-x_rem)/dir.x
|
||||
// We cross y = 0 if dir.y < 0, at dt = -y_rem/dir.y
|
||||
// We cross y = TILE_WIDTH if dir.y > 0, at dt = (TILE_WIDTH-y_rem)/dir.y
|
||||
|
||||
// We can generalize this to:
|
||||
// dx_ind_dir = -1 if dir.x < 0, at dt = -1/dir.x * x_rem + 0.0
|
||||
// dx_ind_dir = 1 if dir.x > 0, at dt = -1/dir.x * x_rem + TILE_WIDTH/dir.x
|
||||
// dx_ind_dir = 0 if dir.x = 0, at dt = 0 * x_rem + INFINITY
|
||||
// dy_ind_dir = -1 if dir.y < 0, at dt = -1/dir.y * y_rem + 0.0
|
||||
// dy_ind_dir = 1 if dir.y > 0, at dt = -1/dir.y * y_rem + TILE_WIDTH/dir.y
|
||||
// dy_ind_dir = 0 if dir.x = 0, at dt = 0 * y_rem + INFINITY
|
||||
|
||||
int dx_ind_dir = 0; |
||||
f32 dx_a = 0.0; |
||||
f32 dx_b = INFINITY; |
||||
if (dir.x < 0) { |
||||
dx_ind_dir = -1; |
||||
dx_a = -1.0f/dir.x; |
||||
dx_b = 0.0; |
||||
} else if (dir.x > 0) { |
||||
dx_ind_dir = 1; |
||||
dx_a = -1.0f/dir.x; |
||||
dx_b = TILE_WIDTH/dir.x; |
||||
} |
||||
|
||||
int dy_ind_dir = 0; |
||||
f32 dy_a = 0.0; |
||||
f32 dy_b = INFINITY; |
||||
if (dir.y < 0) { |
||||
dy_ind_dir = -1; |
||||
dy_a = -1.0f/dir.y; |
||||
dy_b = 0.0; |
||||
} else if (dir.y > 0) { |
||||
dy_ind_dir = 1; |
||||
dy_a = -1.0f/dir.y; |
||||
dy_b = TILE_WIDTH/dir.y; |
||||
} |
||||
|
||||
// Step through cells until we hit an occupied cell
|
||||
int n_steps = 0; |
||||
int dx_ind, dy_ind; |
||||
while (n_steps < 100) { |
||||
n_steps += 1; |
||||
|
||||
f32 dt_best = INFINITY; |
||||
dx_ind = 0; |
||||
dy_ind = 0; |
||||
|
||||
f32 dt_x = dx_a*x_rem + dx_b; |
||||
f32 dt_y = dy_a*y_rem + dy_b; |
||||
if (dt_x < dt_y) { |
||||
dt_best = dt_x; |
||||
dx_ind = dx_ind_dir; |
||||
dy_ind = 0; |
||||
} else { |
||||
dt_best = dt_y; |
||||
dx_ind = 0; |
||||
dy_ind = dy_ind_dir; |
||||
} |
||||
|
||||
// Move up to the next cell
|
||||
x_ind += dx_ind; |
||||
y_ind += dy_ind; |
||||
x_rem += dir.x * dt_best - TILE_WIDTH*dx_ind; |
||||
y_rem += dir.y * dt_best - TILE_WIDTH*dy_ind; |
||||
|
||||
// Check to see if the new cell is solid
|
||||
if (MAPDATA[y_ind*8 + x_ind] > 0) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// Calculate the collision location
|
||||
const v2 collision = { |
||||
TILE_WIDTH*x_ind + x_rem, |
||||
TILE_WIDTH*y_ind + y_rem |
||||
}; |
||||
|
||||
// Calculate the ray length
|
||||
const f32 ray_len = length( ((v2) {collision.x - state.camera_pos.x, collision.y - state.camera_pos.y}) ); |
||||
|
||||
// Calculate the pixel bounds that we fill the wall in for
|
||||
int y_lo = (int)(SCREEN_SIZE_Y/2.0f - cam_len*state.camera_z/ray_len * SCREEN_SIZE_Y / state.camera_height); |
||||
int y_hi = (int)(SCREEN_SIZE_Y/2.0f + cam_len*(WALL_HEIGHT - state.camera_z)/ray_len * SCREEN_SIZE_Y / state.camera_height); |
||||
y_lo = max(y_lo, 0); |
||||
y_hi = min(y_hi, SCREEN_SIZE_Y-1); |
||||
|
||||
u32 color_wall_to_render = (dx_ind == 0) ? color_wall[MAPDATA[y_ind*8 + x_ind]-1] : color_wall_light[MAPDATA[y_ind*8 + x_ind]-1]; |
||||
|
||||
draw_column(x, 0, y_lo-1, color_floor); |
||||
draw_column(x, y_lo, y_hi, color_wall_to_render); |
||||
draw_column(x, y_hi + 1, SCREEN_SIZE_Y-1, color_ceil); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
// Initialize SDL
|
||||
ASSERT( |
||||
SDL_Init(SDL_INIT_VIDEO) == 0, |
||||
"SDL initialization failed: %s\n", |
||||
SDL_GetError() |
||||
); |
||||
|
||||
// Create a window
|
||||
state.window = SDL_CreateWindow( |
||||
"TOOM", |
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(1), |
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(1), |
||||
SCREEN_SIZE_X, |
||||
SCREEN_SIZE_Y, |
||||
SDL_WINDOW_ALLOW_HIGHDPI); |
||||
ASSERT(state.window, "Error creating SDL window: %s\n", SDL_GetError()); |
||||
|
||||
// Create a renderer
|
||||
state.renderer = SDL_CreateRenderer(state.window, -1, SDL_RENDERER_PRESENTVSYNC); |
||||
ASSERT(state.renderer, "Error creating SDL renderer: %s\n", SDL_GetError()); |
||||
|
||||
// Create a texture
|
||||
state.texture = SDL_CreateTexture( |
||||
state.renderer, |
||||
SDL_PIXELFORMAT_ABGR8888, |
||||
SDL_TEXTUREACCESS_STREAMING, |
||||
SCREEN_SIZE_X, |
||||
SCREEN_SIZE_Y); |
||||
ASSERT(state.texture, "Error creating SDL texture: %s\n", SDL_GetError()); |
||||
|
||||
// Init camera
|
||||
state.camera_pos = (v2) { 5.0f, 5.0f }; |
||||
state.camera_dir = ((v2) {cos(0.0), sin(0.0)}); |
||||
state.camera_dir_rotr = rotr((state.camera_dir)); |
||||
state.camera_width = 1.5f; |
||||
state.camera_height = state.camera_width * SCREEN_SIZE_Y / SCREEN_SIZE_X; |
||||
state.camera_z = 0.4; |
||||
|
||||
// Init player state
|
||||
state.player_speed = (v2) { 0.0f, 0.0f }; |
||||
state.player_omega = 0.0f; |
||||
|
||||
// Init keyboard
|
||||
clear_keyboard_state(&state.keyboard_state); |
||||
|
||||
// Time structs
|
||||
struct timeval timeval_start, timeval_end; |
||||
|
||||
// Main loop
|
||||
u32 time_prev_tick = SDL_GetTicks(); |
||||
state.quit = 0; |
||||
while (state.quit == 0) { |
||||
const u32 time_start = SDL_GetTicks(); |
||||
gettimeofday(&timeval_start, NULL); |
||||
|
||||
SDL_Event event; |
||||
while (SDL_PollEvent(&event)) { |
||||
if (event.type == SDL_QUIT) { |
||||
state.quit = 1; |
||||
break; |
||||
} else if (event.type == SDL_KEYDOWN) { |
||||
switch (event.key.keysym.sym) { |
||||
case (SDLK_UP) : state.keyboard_state.up = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_DOWN) : state.keyboard_state.down = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_LEFT) : state.keyboard_state.left = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_RIGHT) : state.keyboard_state.right = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_a) : state.keyboard_state.a = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_s) : state.keyboard_state.s = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_d) : state.keyboard_state.d = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_w) : state.keyboard_state.w = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_q) : state.keyboard_state.q = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_e) : state.keyboard_state.e = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_1) : state.keyboard_state.one = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_2) : state.keyboard_state.two = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_3) : state.keyboard_state.three = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_4) : state.keyboard_state.four = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_5) : state.keyboard_state.five = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_6) : state.keyboard_state.six = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_7) : state.keyboard_state.seven = KeyboardKeyState_Pressed; break; |
||||
case (SDLK_8) : state.keyboard_state.eight = KeyboardKeyState_Pressed; break; |
||||
} |
||||
} else if (event.type == SDL_KEYUP) { |
||||
switch (event.key.keysym.sym) { |
||||
case (SDLK_UP) : state.keyboard_state.up = KeyboardKeyState_Released; break; |
||||
case (SDLK_DOWN) : state.keyboard_state.down = KeyboardKeyState_Released; break; |
||||
case (SDLK_LEFT) : state.keyboard_state.left = KeyboardKeyState_Released; break; |
||||
case (SDLK_RIGHT) : state.keyboard_state.right = KeyboardKeyState_Released; break; |
||||
case (SDLK_a) : state.keyboard_state.a = KeyboardKeyState_Released; break; |
||||
case (SDLK_s) : state.keyboard_state.s = KeyboardKeyState_Released; break; |
||||
case (SDLK_d) : state.keyboard_state.d = KeyboardKeyState_Released; break; |
||||
case (SDLK_w) : state.keyboard_state.w = KeyboardKeyState_Released; break; |
||||
case (SDLK_q) : state.keyboard_state.q = KeyboardKeyState_Released; break; |
||||
case (SDLK_e) : state.keyboard_state.e = KeyboardKeyState_Released; break; |
||||
case (SDLK_1) : state.keyboard_state.one = KeyboardKeyState_Released; break; |
||||
case (SDLK_2) : state.keyboard_state.two = KeyboardKeyState_Released; break; |
||||
case (SDLK_3) : state.keyboard_state.three = KeyboardKeyState_Released; break; |
||||
case (SDLK_4) : state.keyboard_state.four = KeyboardKeyState_Released; break; |
||||
case (SDLK_5) : state.keyboard_state.five = KeyboardKeyState_Released; break; |
||||
case (SDLK_6) : state.keyboard_state.six = KeyboardKeyState_Released; break; |
||||
case (SDLK_7) : state.keyboard_state.seven = KeyboardKeyState_Released; break; |
||||
case (SDLK_8) : state.keyboard_state.eight = KeyboardKeyState_Released; break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// TODO: Move to more accurate timing?
|
||||
const u32 time_tick_start = SDL_GetTicks(); |
||||
const f32 dt = (time_tick_start - time_prev_tick) / 1000.0f; |
||||
tick(dt); |
||||
time_prev_tick = time_tick_start; |
||||
|
||||
render(); |
||||
|
||||
decay_keyboard_state(&state.keyboard_state); |
||||
|
||||
// Get timer end for all the non-SDL stuff
|
||||
gettimeofday(&timeval_end, NULL); |
||||
f64 game_ms_elapsed = (timeval_end.tv_sec - timeval_start.tv_sec) * 1000.0; // sec to ms
|
||||
game_ms_elapsed += (timeval_end.tv_usec - timeval_start.tv_usec) / 1000.0; // us to ms
|
||||
// printf("Game: %.3f ms, %.1f fps\n", game_ms_elapsed, 1000.0f / max(1.0f, game_ms_elapsed));
|
||||
|
||||
SDL_UpdateTexture(state.texture, NULL, state.pixels, SCREEN_SIZE_X * 4); |
||||
SDL_RenderCopyEx( |
||||
state.renderer, |
||||
state.texture, |
||||
NULL, |
||||
NULL, |
||||
0.0, |
||||
NULL, |
||||
SDL_FLIP_VERTICAL); |
||||
|
||||
// SDL_RENDERER_PRESENTVSYNC means this is syncronized with the monitor refresh rate. (30Hz)
|
||||
SDL_RenderPresent(state.renderer); |
||||
|
||||
const u32 time_end = SDL_GetTicks(); |
||||
const u32 ms_elapsed = time_end - time_start; |
||||
const f32 fps = 1000.0f / max(1, ms_elapsed); |
||||
// printf("FPS: %.1f\n", fps);
|
||||
} |
||||
|
||||
SDL_DestroyWindow(state.window); |
||||
return 0; |
||||
} |
@ -0,0 +1,13 @@ |
||||
[wrap-file] |
||||
directory = flac-1.4.3 |
||||
source_url = https://github.com/xiph/flac/releases/download/1.4.3/flac-1.4.3.tar.xz |
||||
source_filename = flac-1.4.3.tar.xz |
||||
source_hash = 6c58e69cd22348f441b861092b825e591d0b822e106de6eb0ee4d05d27205b70 |
||||
patch_filename = flac_1.4.3-2_patch.zip |
||||
patch_url = https://wrapdb.mesonbuild.com/v2/flac_1.4.3-2/get_patch |
||||
patch_hash = 3eace1bd0769d3e0d4ff099960160766a5185d391c8f583293b087a1f96c2a9c |
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/flac_1.4.3-2/flac-1.4.3.tar.xz |
||||
wrapdb_version = 1.4.3-2 |
||||
|
||||
[provide] |
||||
flac = flac_dep |
@ -0,0 +1,13 @@ |
||||
[wrap-file] |
||||
directory = libogg-1.3.5 |
||||
source_url = https://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.xz |
||||
source_filename = libogg-1.3.5.tar.xz |
||||
source_hash = c4d91be36fc8e54deae7575241e03f4211eb102afb3fc0775fbbc1b740016705 |
||||
patch_filename = ogg_1.3.5-6_patch.zip |
||||
patch_url = https://wrapdb.mesonbuild.com/v2/ogg_1.3.5-6/get_patch |
||||
patch_hash = 8be6dcd5f93bbf9c0b9c8ec1fa29810226a60f846383074ca05b313a248e78b2 |
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/ogg_1.3.5-6/libogg-1.3.5.tar.xz |
||||
wrapdb_version = 1.3.5-6 |
||||
|
||||
[provide] |
||||
ogg = libogg_dep |
@ -0,0 +1,13 @@ |
||||
[wrap-file] |
||||
directory = openal-soft-1.23.1 |
||||
source_url = https://github.com/kcat/openal-soft/archive/refs/tags/1.23.1.tar.gz |
||||
source_filename = openal-soft-1.23.1.tar.gz |
||||
source_hash = dfddf3a1f61059853c625b7bb03de8433b455f2f79f89548cbcbd5edca3d4a4a |
||||
patch_filename = openal-soft_1.23.1-2_patch.zip |
||||
patch_url = https://wrapdb.mesonbuild.com/v2/openal-soft_1.23.1-2/get_patch |
||||
patch_hash = e03c3afe0bb40a931d25d41d92a08b90e3c33b217d1b47210b26ca6627eb3aa3 |
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/openal-soft_1.23.1-2/openal-soft-1.23.1.tar.gz |
||||
wrapdb_version = 1.23.1-2 |
||||
|
||||
[provide] |
||||
openal = openal_dep |
@ -0,0 +1,13 @@ |
||||
[wrap-file] |
||||
directory = SFML-2.6.2 |
||||
source_url = https://github.com/SFML/SFML/archive/refs/tags/2.6.2.tar.gz |
||||
source_filename = 2.6.2.tar.gz |
||||
source_hash = 15ff4d608a018f287c6a885db0a2da86ea389e516d2323629e4d4407a7ce047f |
||||
patch_filename = sfml_2.6.2-1_patch.zip |
||||
patch_url = https://wrapdb.mesonbuild.com/v2/sfml_2.6.2-1/get_patch |
||||
patch_hash = 36737f7fc6d616be791c6901b15414315b3a77df82dabc80b151d628e5d48386 |
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sfml_2.6.2-1/2.6.2.tar.gz |
||||
wrapdb_version = 2.6.2-1 |
||||
|
||||
[provide] |
||||
sfml = sfml_dep |
@ -0,0 +1,14 @@ |
||||
[wrap-file] |
||||
directory = libvorbis-1.3.7 |
||||
source_url = https://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz |
||||
source_filename = libvorbis-1.3.7.tar.xz |
||||
source_hash = b33cc4934322bcbf6efcbacf49e3ca01aadbea4114ec9589d1b1e9d20f72954b |
||||
patch_filename = vorbis_1.3.7-4_patch.zip |
||||
patch_url = https://wrapdb.mesonbuild.com/v2/vorbis_1.3.7-4/get_patch |
||||
patch_hash = 979e22b24b16c927040700dfd8319cd6ba29bf52a14dbc66b1cb4ea60504f14a |
||||
wrapdb_version = 1.3.7-4 |
||||
|
||||
[provide] |
||||
vorbis = vorbis_dep |
||||
vorbisfile = vorbisfile_dep |
||||
vorbisenc = vorbisenc_dep |
Loading…
Reference in new issue