First cut at a replica of the python raycaster. Left side almost works the same but have to sort out math differences.
This commit is contained in:
		
							parent
							
								
									6b181382bd
								
							
						
					
					
						commit
						ca80736d7c
					
				
					 21 changed files with 2165 additions and 90 deletions
				
			
		
							
								
								
									
										40
									
								
								dbc.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								dbc.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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}; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										29
									
								
								dbc.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								dbc.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||
| } | ||||
							
								
								
									
										34
									
								
								main.cpp
									
										
									
									
									
								
							
							
						
						
									
										34
									
								
								main.cpp
									
										
									
									
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										94
									
								
								matrix.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								matrix.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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"); | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										305
									
								
								matrix.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								matrix.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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>; | ||||
| } | ||||
							
								
								
									
										34
									
								
								meson.build
									
										
									
									
									
								
							
							
						
						
									
										34
									
								
								meson.build
									
										
									
									
									
								
							|  | @ -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) | ||||
|  |  | |||
							
								
								
									
										19
									
								
								point.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								point.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										126
									
								
								pycaster.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								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) | ||||
							
								
								
									
										102
									
								
								raycaster.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								raycaster.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
|  | @ -46,6 +46,7 @@ QuickCG can handle some things that standard C++ doesn't but that are commonly u | |||
| #include <map> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <cassert> | ||||
| 
 | ||||
| namespace QuickCG | ||||
| { | ||||
|  | @ -65,7 +66,7 @@ namespace QuickCG | |||
| 	SDL_Renderer* render; | ||||
| 	SDL_Texture* tex; | ||||
| 	SDL_PixelFormat *fmt; | ||||
| 	const Uint8* inkeys; | ||||
| 	const Uint8* inkeys = NULL; | ||||
| 	SDL_Event event = { 0 }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -76,7 +77,9 @@ namespace QuickCG | |||
| 
 | ||||
| 	bool keyDown(int key) //this checks if the key is held down, returns true all the time until the key is up
 | ||||
| 	{ | ||||
| 		return (inkeys[key] != 0); | ||||
|         assert(inkeys != NULL && "inkeys is not initialized!"); | ||||
| 		// return (inkeys[key] != 0);
 | ||||
|         return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bool keyPressed(int key) //this checks if the key is *just* pressed, returns true only once until the key is up again
 | ||||
|  | @ -171,6 +174,8 @@ namespace QuickCG | |||
| 			SDL_Quit(); | ||||
| 			std::exit(1); | ||||
| 		} | ||||
| 
 | ||||
| 		inkeys = SDL_GetKeyboardState(NULL); | ||||
| 	} | ||||
| 
 | ||||
| 	//Locks the screen
 | ||||
|  | @ -79,6 +79,7 @@ int main(int /*argc*/, char */*argv*/[]) | |||
|   double oldTime = 0; //time of previous frame
 | ||||
| 
 | ||||
|   screen(screenWidth, screenHeight, 0, "Raycaster"); | ||||
| 
 | ||||
|   while(!done()) | ||||
|   { | ||||
|     for(int x = 0; x < w; x++) | ||||
|  | @ -176,7 +177,7 @@ int main(int /*argc*/, char */*argv*/[]) | |||
|       if(drawEnd >= h) drawEnd = h - 1; | ||||
| 
 | ||||
|       //choose wall color
 | ||||
|       ColorRGB color; | ||||
|       ColorRGBA color; | ||||
|       switch(worldMap[mapX][mapY]) | ||||
|       { | ||||
|         case 1:  color = RGB_Red;    break; //red
 | ||||
|  | @ -198,45 +199,52 @@ int main(int /*argc*/, char */*argv*/[]) | |||
|     double frameTime = (time - oldTime) / 1000.0; //frameTime is the time this frame has taken, in seconds
 | ||||
|     print(1.0 / frameTime); //FPS counter
 | ||||
|     redraw(); | ||||
|     cls(); | ||||
| 
 | ||||
|     //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
 | ||||
|     readKeys(); | ||||
|     //move forward if no wall in front of you
 | ||||
|     if(keyDown(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(keyDown(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(keyDown(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(keyDown(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); | ||||
| 
 | ||||
|     SDL_Event event; | ||||
|     while(SDL_PollEvent(&event)) { | ||||
|       if(event.type != SDL_KEYDOWN) continue; | ||||
| 
 | ||||
|       cls(); | ||||
|       //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; | ||||
| } | ||||
							
								
								
									
										471
									
								
								scratchpad/raycaster_sprites.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										471
									
								
								scratchpad/raycaster_sprites.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										293
									
								
								scratchpad/raycaster_textured.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								scratchpad/raycaster_textured.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										553
									
								
								scratchpad/timcaster.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										553
									
								
								scratchpad/timcaster.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
							
								
								
									
										13
									
								
								wraps/flac.wrap
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								wraps/flac.wrap
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										13
									
								
								wraps/ogg.wrap
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								wraps/ogg.wrap
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										13
									
								
								wraps/openal-soft.wrap
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								wraps/openal-soft.wrap
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										13
									
								
								wraps/sfml.wrap
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								wraps/sfml.wrap
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										14
									
								
								wraps/vorbis.wrap
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								wraps/vorbis.wrap
									
										
									
									
									
										Normal file
									
								
							|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Zed A. Shaw
						Zed A. Shaw