├── LICENSE ├── README.md ├── ckube.c ├── gifs ├── 01.gif ├── 02.gif ├── 03.gif ├── 04.gif ├── 05.gif ├── 06.gif ├── 07.gif ├── simple01.gif └── simple02.gif └── makefile /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 pablo peñarroja 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ckube 2 | **_raymarch cubes in your unix terminal_** 3 | 4 | 5 | 6 | *`ckube -V 5.0 -M 0.1 -C 8.0`* 7 | 8 | ![](gifs/02.gif) 9 | *`ckube -H 4.0 -m 0.1`* 10 | 11 | ### install 12 | *__ncurses is required on your system.__* 13 | *__your terminal must support colors.__* 14 | ``` 15 | git clone https://github.com/soybin/ckube 16 | cd ckube 17 | make install 18 | ``` 19 | 20 | ### usage 21 | you can press enter to pause the rendering at any point. press enter again to resume it. 22 | you can combine different commands to accomplish very different results. here is a list of the currently available commands, the argument they expect, what they do, and their default value in case they're not set: 23 | flag | argument data type | what it does | default value 24 | -----|--------------------|--------------|-------------- 25 | -r | no argument required | set some of the values randomly in a way that the scene ends up looking nice | not set 26 | -c | integer | color palette change (0 - 4) 4th palette is monochrome | 0 27 | -1 | integer | set first rendering character to any unicode character | 9608 (█) 28 | -2 | integer | set second rendering character to any unicode character | 9608 (█) 29 | -3 | integer | set third rendering character to any unicode character | 9608 (█) 30 | -h | no argument required | print help | not set 31 | -H | float | distance between every cube on the x-axis. note that any value other than zero will create an infinite line of cubes with the specified separation value. a minimum value of 4 is recommended, otherwise the cubes will overlap infinitely | 0.0 32 | -V | float | distance between every cube on the y-axis. note that any value other than zero will create an infinite line of cubes with the specified separation value. a minimum value of 4 is recommended, otherwise the cubes will overlap infinitely | 0.0 33 | -m | float | lineraly move the camera across the x-axis the specified amount of units per frame. if there's a single cube (in other words, -H is set to 0.0), the camera will lose track of the scene | 0.0 34 | -M | float | lineraly move the camera across the y-axis the specified amount of units per frame. if there's a single cube (in other words, -V is set to 0.0), the camera will lose track of the scene | 0.0 35 | -C | float | distance from the z-axis position of the camera, to the origin of coordinates (how far away is the camera from the scene) | 6.0 36 | -P | integer | degrees added to the pitch axis rotation per frame | random between [0, 5] 37 | -Y | integer | degrees added to the yaw axis rotation per frame | random between [0, 5] 38 | -R | integer | degrees added to the roll axis rotation per frame | random between [0, 5] 39 | -f | integer | frames per second (fps) | 20 40 | -F | integer | field of view (fov) | 40 41 | -s | float | vertical stretch factor for the rendered image. this will depend on your terminal cursor aspect ratio | 2.0 42 | -S | integer | maximum amount of steps allowed when raymarching a pixel. adjusting this value will considerably affect performance | 32 43 | -D | float | intersection distance for a ray to count as an intersection | 1e-3 44 | 45 | *note that you may set the '-r' flag, and then overwrite any randomly set value as you want* 46 | 47 | -------------------------------------------------------------------------------- /ckube.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * ckube.c 4 | * Copyright (c) 2020 Pablo Peñarroja 5 | */ 6 | 7 | #define _XOPEN_SOURCE_EXTENDED 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /* */ 19 | /*-------- m a t h --------*/ 20 | /* */ 21 | 22 | #define M_PI 3.14159265358979323846 23 | 24 | #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) 25 | #define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) 26 | #define ABS(x) ((x) < 0 ? -(x) : (x)) 27 | 28 | typedef struct _float3 { 29 | float x; 30 | float y; 31 | float z; 32 | } float3; 33 | 34 | typedef struct _mat3_3{ 35 | float3 x; 36 | float3 y; 37 | float3 z; 38 | } mat3_3; 39 | 40 | static inline float3 float3_add(float3 l, float3 r) { 41 | return (float3){ l.x + r.x, l.y + r.y, l.z + r.z }; 42 | } 43 | 44 | static inline float3 float3_addf(float3 l, float r) { 45 | return (float3){ l.x + r, l.y + r, l.z + r }; 46 | } 47 | 48 | static inline float3 float3_sub(float3 l, float3 r) { 49 | return (float3){ l.x - r.x, l.y - r.y, l.z - r.z }; 50 | } 51 | 52 | static inline float3 float3_subf(float3 l, float r) { 53 | return (float3){ l.x - r, l.y - r, l.z - r }; 54 | } 55 | 56 | static inline float3 float3_mult(float3 l, float3 r) { 57 | return (float3){ l.x * r.x, l.y * r.y, l.z * l.z }; 58 | } 59 | 60 | static inline float3 float3_multf(float3 l, float r) { 61 | return (float3){ l.x * r, l.y * r, l.z * r }; 62 | } 63 | 64 | static inline float3 float3_div(float3 l, float3 r) { 65 | return (float3){ l.x / r.x, l.y / r.y, l.z / r.z }; 66 | } 67 | 68 | static inline float3 float3_divf(float3 l, float r) { 69 | return (float3){ l.x / r, l.y / r, l.z / r }; 70 | } 71 | 72 | static inline float float3_length(float3 v) { 73 | return sqrt(v.x * v.x + v.y * v.y + v.z * v.z); 74 | } 75 | 76 | static inline float3 float3_normalize(float3 v) { 77 | float length = float3_length(v); 78 | float3 ret = { v.x / length, v.y / length, v.z / length }; 79 | return ret; 80 | } 81 | 82 | static inline float float3_dot(float3 l, float3 r) { 83 | return l.x * r.x + l.y * r.y + l.z * r.z; 84 | } 85 | 86 | static inline float3 float3_abs(float3 v) { 87 | float3 ret = { ABS(v.x), ABS(v.y), ABS(v.z) }; 88 | return ret; 89 | } 90 | 91 | static inline float3 float3_min(float3 l, float3 r) { 92 | float3 ret = { MIN(l.x, r.x), MIN(l.y, r.y), MIN(l.z, r.z) }; 93 | return ret; 94 | } 95 | 96 | static inline float3 float3_max(float3 l, float3 r) { 97 | float3 ret = { MAX(l.x, r.x), MAX(l.y, r.y), MAX(l.z, r.z) }; 98 | return ret; 99 | } 100 | 101 | static inline float3 float3_maxf(float3 l, float r) { 102 | float3 ret = { MAX(l.x, r), MAX(l.y, r), MAX(l.z, r) }; 103 | return ret; 104 | } 105 | 106 | /* multiply by 3x3 matrix */ 107 | static inline float3 float3_mult_mat3_3(float3 l, mat3_3 r) { 108 | return (float3){ 109 | l.x * r.x.x + l.y * r.y.x + l.z * r.z.x, 110 | l.x * r.x.y + l.y * r.y.y + l.z * r.z.y, 111 | l.x * r.x.z + l.y * r.y.z + l.z * r.z.z 112 | }; 113 | } 114 | 115 | /* float mod operator */ 116 | static inline float float_mod(float l, float r) { 117 | return l - r * floor(l / (r != 0.0f ? r : 1.0f)); 118 | } 119 | 120 | /* random integer in range */ 121 | static inline int int_random_range(int min_range, int max_range) { 122 | return (rand() % (max_range - min_range + 1)) + min_range; 123 | } 124 | 125 | /* random float in range */ 126 | static inline float float_random_range(float min_range, float max_range) { 127 | return ((float)rand()/(float)(RAND_MAX)) * (max_range - min_range) + min_range; 128 | } 129 | 130 | /* */ 131 | /*-------- r a y m a r c h i n g --------*/ 132 | /* */ 133 | 134 | /* cube distance estimator */ 135 | static inline float de_cube(float3 point) { 136 | float3 a = float3_subf(float3_abs(point), 1.0f); 137 | return float3_length(float3_maxf(a, 0.0f)) + MIN(MAX(a.x, MAX(a.y, a.z)), 0.0f); 138 | } 139 | 140 | /* */ 141 | /*-------- a p p l i c a t i o n --------*/ 142 | /* */ 143 | 144 | void print_help() { 145 | printf("%s\n", " _____ __ __ __ __ ___ ____ "); 146 | printf("%s\n", " / ___/ / //_/ / / / / / _ ) / __/ "); 147 | printf("%s\n", " / /__ / < / /_/ / / _ | / _/ "); 148 | printf("%s\n", " /____/ /_//_/ /_____/ /____/ /___/ "); 149 | printf("%s\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 150 | printf("%s\n", " | press space to pause rendering | "); 151 | printf("%s\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 152 | printf("%s\n", " flag [arg] | what is it | defaul value "); 153 | printf("%s\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 154 | printf("%s\n", "-r -> random settings -> false"); 155 | printf("%s\n", "-c [int] -> color pallette (0 - 4) -> 0"); 156 | printf("%s\n", "-1 [int] -> first unicode render char ->█(9608)"); 157 | printf("%s\n", "-2 [int] -> second unicode render char ->█(9608)"); 158 | printf("%s\n", "-3 [int] -> third unicode render char ->█(9608)"); 159 | printf("%s\n", "-h -> print this menu -> false"); 160 | printf("%s\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 161 | printf("%s\n", "-H [float] -> horizontal separation -> 0.0"); 162 | printf("%s\n", "-V [float] -> vertical separation -> 0.0"); 163 | printf("%s\n", "-m [float] -> move camera horizontally -> 0.0"); 164 | printf("%s\n", "-M [float] -> move camera vertically -> 0.0"); 165 | printf("%s\n", "-C [float] -> camera distance in z axis -> 6.0"); 166 | printf("%s\n", "-P [int] -> pitch in degrees per frame -> random"); 167 | printf("%s\n", "-Y [int] -> yaw in degrees per frame -> random"); 168 | printf("%s\n", "-R [int] -> roll in degrees per frame -> random"); 169 | printf("%s\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 170 | printf("%s\n", "-f [int] -> frames per second -> 20"); 171 | printf("%s\n", "-F [int] -> field of view -> 40"); 172 | printf("%s\n", "-s [float] -> vertical stretch -> 2.0"); 173 | printf("%s\n", "-S [int] -> raymarching max steps -> 32"); 174 | printf("%s\n", "-D [float] -> intersection distance -> 1e-3"); 175 | printf("%s\n", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 176 | } 177 | 178 | int main(int argc, char* argv[]) { 179 | 180 | /* allow utf-8 */ 181 | setlocale(LC_ALL, ""); 182 | 183 | /* seed random functions */ 184 | srand((unsigned int)time(NULL)); 185 | 186 | /*---- d e c l a r e v a r s ----*/ 187 | 188 | /* application */ 189 | unsigned int run = 3; /* first bit -> exit? | second bit -> pause rendering? */ 190 | unsigned int cols; 191 | unsigned int rows; 192 | unsigned int keypress; 193 | unsigned int frame_count = 0; 194 | /* renderer */ 195 | unsigned int fps = 20; 196 | unsigned int fov = 40; 197 | unsigned int max_step = 32; 198 | float min_dist = 1e-3; 199 | float y_stretch_factor = 2.0f; 200 | /* scene */ 201 | int geometry_rotation_x = -1; 202 | int geometry_rotation_y = -1; 203 | int geometry_rotation_z = -1; 204 | int color_one = 1; /* red */ 205 | int color_two = 2; /* green */ 206 | int color_three = 4; /* blue */ 207 | int color_background = 0; 208 | float geometry_repetition_x = 0.0f; 209 | float geometry_repetition_y = 0.0f; 210 | float half_geometry_repetition_x = 0.0f; 211 | float half_geometry_repetition_y = 0.0f; 212 | float camera_distance = 6.0f; 213 | float camera_movement_x = 0.0f; 214 | float camera_movement_y = 0.0f; 215 | wchar_t drawing_glyphs[3] = { L'█', L'█', L'█' }; 216 | 217 | /*---- a r g u m e n t s ----*/ 218 | 219 | for (int i = 1; i < argc; ++i) { 220 | if (argv[i][0] != '-' || strlen(argv[i]) != 2) { 221 | printf("%s\n", "[-] Invalid argument. Printing argument list."); 222 | print_help(); 223 | return 1; 224 | } 225 | switch (argv[i][1]) { 226 | case 'r': /* random assignment */ 227 | fov = int_random_range(40, 60); 228 | if (int_random_range(0, 1)) { 229 | geometry_repetition_x = float_random_range(4.0f, 6.0f); 230 | half_geometry_repetition_x = geometry_repetition_x / 2.0f; 231 | camera_movement_x = float_random_range(-0.1f, 0.1f); 232 | } 233 | if (int_random_range(0, 1)) { 234 | geometry_repetition_y = float_random_range(4.0f, 6.0f); 235 | half_geometry_repetition_y = geometry_repetition_y / 2.0f; 236 | camera_movement_y = float_random_range(-0.1f, 0.1f); 237 | } 238 | camera_distance = float_random_range(4.0f, 8.0f); 239 | color_one = int_random_range(1, 7); 240 | int j; 241 | for (j = int_random_range(1, 7); j == color_one; j = int_random_range(1, 7)); 242 | color_two = j; 243 | for (j = int_random_range(1, 7); j == color_one || j == color_two; j = int_random_range(1, 7)); 244 | color_three = j; 245 | color_background = 0; 246 | break; 247 | case 'c': /* color pallette */ 248 | /* 249 | * COLOR_BLACK 0 250 | * COLOR_RED 1 251 | * COLOR_GREEN 2 252 | * COLOR_YELLOW 3 253 | * COLOR_BLUE 4 254 | * COLOR_MAGENTA 5 255 | * COLOR_CYAN 6 256 | * COLOR_WHITE 7 257 | */ 258 | switch (atoi(argv[++i]) % 5) { 259 | case 1: 260 | color_one = 3; 261 | color_two = 5; 262 | color_three = 6; 263 | color_background = 0; 264 | break; 265 | case 2: 266 | color_one = 4; 267 | color_two = 2; 268 | color_three = 7; 269 | color_background = 0; 270 | break; 271 | case 3: 272 | color_one = 3; 273 | color_two = 5; 274 | color_three = 6; 275 | break; 276 | case 4: 277 | color_one = 7; 278 | color_two = 7; 279 | color_three = 7; 280 | color_background = 0; 281 | } 282 | break; 283 | case '1': 284 | drawing_glyphs[0] = (wchar_t)atof(argv[++i]); 285 | break; 286 | case '2': 287 | drawing_glyphs[1] = (wchar_t)atoi(argv[++i]); 288 | break; 289 | case '3': 290 | drawing_glyphs[2] = (wchar_t)atoi(argv[++i]); 291 | break; 292 | case 'h': 293 | print_help(); 294 | return 1; 295 | break; 296 | case 'H': 297 | geometry_repetition_x = atof(argv[++i]); 298 | half_geometry_repetition_x = geometry_repetition_x / 2.0f; 299 | break; 300 | case 'V': 301 | geometry_repetition_y = atof(argv[++i]); 302 | half_geometry_repetition_y = geometry_repetition_y / 2.0f; 303 | break; 304 | case 'm': 305 | camera_movement_x = atof(argv[++i]); 306 | break; 307 | case 'M': 308 | camera_movement_y = atof(argv[++i]); 309 | break; 310 | case 'C': 311 | camera_distance = atof(argv[++i]); 312 | break; 313 | case 'P': /* pitch rotation */ 314 | geometry_rotation_x = atoi(argv[++i]); 315 | break; 316 | case 'Y': /* yaw rotation */ 317 | geometry_rotation_y = atoi(argv[++i]); 318 | break; 319 | case 'R': /* roll rotation */ 320 | geometry_rotation_z = atoi(argv[++i]); 321 | break; 322 | case 'f': 323 | fps = atoi(argv[++i]); 324 | break; 325 | case 'F': 326 | fov = atoi(argv[++i]); 327 | break; 328 | case 's': 329 | y_stretch_factor = (float)atof(argv[++i]); 330 | break; 331 | case 'S': 332 | max_step = atoi(argv[++i]); 333 | break; 334 | case 'D': 335 | min_dist = atof(argv[++i]); 336 | break; 337 | default: 338 | print_help(); 339 | return 1; 340 | } 341 | } 342 | 343 | /* assign rotations if not provided by the user */ 344 | if (geometry_rotation_x < 0 && geometry_rotation_y < 0 && geometry_rotation_z < 0) { 345 | geometry_rotation_x = int_random_range(0, 5); 346 | geometry_rotation_y = int_random_range(0, 5); 347 | geometry_rotation_z = int_random_range(0, 5); 348 | } 349 | 350 | /*---- i n i t ----*/ 351 | 352 | /* init ncurses */ 353 | initscr(); 354 | noecho(); 355 | curs_set(0); 356 | timeout(0); 357 | 358 | /* check if terminal supports color */ 359 | if (!has_colors()) { 360 | printf("%s\n", "[-] Your terminal doesn't support colors. Exiting ckube"); 361 | return 1; 362 | } 363 | 364 | /* configure colors */ 365 | start_color(); 366 | init_pair(1, color_one, color_background); 367 | init_pair(2, color_two, color_background); 368 | init_pair(3, color_three, color_background); 369 | 370 | /* 371 | * holds the unitary vector direction 372 | * for every pixel in the terminal 373 | */ 374 | float3* direction_matrix = NULL; 375 | 376 | /* 377 | * holds precomputed rotation matrices 378 | * for every stage of the cube's rotation. 379 | * sin and cos operations are expensive 380 | */ 381 | int ratios_x_size = geometry_rotation_x > 0 ? (360 / geometry_rotation_x) : 1; 382 | int ratios_y_size = geometry_rotation_y > 0 ? (360 / geometry_rotation_y) : 1; 383 | int ratios_z_size = geometry_rotation_z > 0 ? (360 / geometry_rotation_z) : 1; 384 | float sin_x[ratios_x_size]; 385 | float cos_x[ratios_x_size]; 386 | float sin_y[ratios_y_size]; 387 | float cos_y[ratios_y_size]; 388 | float sin_z[ratios_z_size]; 389 | float cos_z[ratios_z_size]; 390 | /* pitch */ 391 | for (int i = 0; i < ratios_x_size; ++i) { 392 | float rotation_x = geometry_rotation_x * i; 393 | /* to radians */ 394 | rotation_x *= M_PI / 180.0f; 395 | /* store ratios */ 396 | sin_x[i] = sin(rotation_x); 397 | cos_x[i] = cos(rotation_x); 398 | } 399 | /* yaw */ 400 | for (int i = 0; i < ratios_y_size; ++i) { 401 | float rotation_y = geometry_rotation_y * i; 402 | /* to radians */ 403 | rotation_y *= M_PI / 180.0f; 404 | /* store ratios */ 405 | sin_y[i] = sin(rotation_y); 406 | cos_y[i] = cos(rotation_y); 407 | } 408 | /* roll */ 409 | for (int i = 0; i < ratios_z_size; ++i) { 410 | float rotation_z = geometry_rotation_z * i; 411 | /* to radians */ 412 | rotation_z *= M_PI / 180.0f; 413 | /* store ratios */ 414 | sin_z[i] = sin(rotation_z); 415 | cos_z[i] = cos(rotation_z); 416 | } 417 | 418 | /* compute frame duration */ 419 | float time_per_frame = 1.0f / (float)fps; 420 | 421 | /* first frame timestamp */ 422 | clock_t previous_time = clock(); 423 | 424 | /*---- m a i n l o o p ----*/ 425 | 426 | for (float3 ori = { 0.0f, 0.0f, camera_distance }; run & (1 << 0); ) { 427 | 428 | /*---- u s e r i n p u t ----*/ 429 | 430 | /* get out */ 431 | if ((keypress = wgetch(stdscr)) != ERR) { 432 | switch (keypress) { 433 | case ' ': /* spacebar */ 434 | run ^= (1 << 1); 435 | break; 436 | case 27: /* escape */ 437 | case 'q': 438 | run ^= (1 << 0); 439 | break; 440 | } 441 | } 442 | 443 | /* resolution change */ 444 | int temp_rows; 445 | int temp_cols; 446 | getmaxyx(stdscr, temp_rows, temp_cols); 447 | if (rows != temp_rows || cols != temp_cols) { 448 | rows = temp_rows; 449 | cols = temp_cols; 450 | free(direction_matrix); 451 | direction_matrix = (float3*)malloc(rows * cols * sizeof(float3)); 452 | /* fill up ray directions matrix */ 453 | for (int r = 0; r < rows; ++r) { 454 | for (int c = 0; c < cols; ++c) { 455 | float3 dir; 456 | dir.y = (float)r * y_stretch_factor + 0.5f - rows * y_stretch_factor / 2.0f; 457 | dir.x = (float)c + 0.5f - cols / 2.0f; 458 | dir.z = -(float)rows / tan(fov * M_PI / 180.0f / 2.0f); 459 | dir = float3_normalize(dir); 460 | *(direction_matrix + r * cols + c) = dir; 461 | } 462 | } 463 | } 464 | 465 | /* check if rendering is paused */ 466 | 467 | if (run & (1 << 1)) { 468 | 469 | /*---- r e n d e r i n g ----*/ 470 | 471 | /* update ray origin for this frame */ 472 | ori.x += camera_movement_x; 473 | ori.y += camera_movement_y; 474 | 475 | /* compute general rotation matrix */ 476 | int pos_x = frame_count % ratios_x_size; 477 | int pos_y = frame_count % ratios_y_size; 478 | int pos_z = frame_count % ratios_z_size; 479 | mat3_3 general_rotation_matrix = (mat3_3) { 480 | (float3) { cos_z[pos_z] * cos_y[pos_y], 481 | cos_z[pos_z] * sin_y[pos_y] * sin_x[pos_x] - sin_z[pos_z] * cos_x[pos_x], 482 | cos_z[pos_z] * sin_y[pos_y] * cos_x[pos_x] + sin_z[pos_z] * sin_x[pos_x] }, 483 | (float3) { sin_z[pos_z] * cos_y[pos_y], 484 | sin_z[pos_z] * sin_y[pos_y] * sin_x[pos_x] + cos_z[pos_z] * cos_x[pos_x], 485 | sin_z[pos_z] * sin_y[pos_y] * cos_x[pos_x] - cos_z[pos_z] * sin_x[pos_x] }, 486 | (float3) { -sin_y[pos_y], 487 | cos_y[pos_y] * sin_x[pos_x], 488 | cos_y[pos_y] * cos_x[pos_x] } 489 | }; 490 | 491 | int previous_normal_id = 0; 492 | for (int r = 0; r < rows; ++r) { 493 | previous_normal_id = 0; 494 | for (int c = 0; c < cols; ++c) { 495 | /* get pixel ray direction */ 496 | float3 dir = *(direction_matrix + r * cols + c); 497 | float3 point; 498 | /* raymarch */ 499 | int step = 0; 500 | for (float total_dist = 0.0f; step < max_step; ++step) { 501 | /* compute intersection */ 502 | point = float3_add(ori, float3_multf(dir, total_dist)); 503 | /* apply infinity using modulo */ 504 | point.x += half_geometry_repetition_x; 505 | point.y += half_geometry_repetition_y; 506 | point.x = float_mod(point.x, geometry_repetition_x); 507 | point.y = float_mod(point.y, geometry_repetition_y); 508 | point.x -= half_geometry_repetition_x; 509 | point.y -= half_geometry_repetition_y; 510 | /* apply rotation */ 511 | point = float3_mult_mat3_3(point, general_rotation_matrix); 512 | /* get distance */ 513 | float dist = de_cube(point); 514 | if (dist < min_dist) { 515 | break; 516 | } 517 | total_dist += dist; 518 | } 519 | /* in case object was hit, draw */ 520 | wchar_t draw[1] = L" "; 521 | if (step < max_step) { 522 | 523 | /*---- n o r m a l ----*/ 524 | 525 | const float h = 1e-4; 526 | const float3 xyy = { 1.0f, -1.0f, -1.0f }; 527 | const float3 yyx = { -1.0f, -1.0f, 1.0f }; 528 | const float3 yxy = { -1.0f, 1.0f, -1.0f }; 529 | const float3 xxx = { 1.0f, 1.0f, 1.0f }; 530 | float3 normal = float3_normalize( 531 | float3_add( 532 | float3_add( 533 | float3_multf(xyy, de_cube(float3_add(point, float3_multf(xyy, h)))), 534 | float3_multf(yyx, de_cube(float3_add(point, float3_multf(yyx, h)))) 535 | ), 536 | float3_add( 537 | float3_multf(yxy, de_cube(float3_add(point, float3_multf(yxy, h)))), 538 | float3_multf(xxx, de_cube(float3_add(point, float3_multf(xxx, h)))) 539 | ) 540 | ) 541 | ); 542 | /* get normal id */ 543 | int normal_id = abs((int)normal.x) * 1 + abs((int)normal.y) * 2 + abs((int)normal.z) * 3; 544 | if (normal_id) { 545 | attron(COLOR_PAIR(normal_id)); 546 | draw[0] = (wchar_t)drawing_glyphs[normal_id - 1]; 547 | previous_normal_id = normal_id; 548 | } else if (previous_normal_id){ 549 | attron(COLOR_PAIR(previous_normal_id)); 550 | draw[0] = (wchar_t)drawing_glyphs[previous_normal_id - 1]; 551 | } 552 | } 553 | /* draw character to screen matrix */ 554 | mvaddwstr(r, c, draw); 555 | } 556 | } 557 | ++frame_count; 558 | } 559 | 560 | /*---- f p s l i m i t ----*/ 561 | 562 | long double delta_time = (long double)(clock() - previous_time) / CLOCKS_PER_SEC; 563 | long int time_remaining = (time_per_frame - delta_time) * 1e6; 564 | if (time_remaining > 0) { 565 | usleep(time_remaining); 566 | } 567 | previous_time = clock(); 568 | } 569 | 570 | /*---- c l e a n u p ----*/ 571 | 572 | free(direction_matrix); 573 | endwin(); 574 | 575 | return 0; 576 | } 577 | -------------------------------------------------------------------------------- /gifs/01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/01.gif -------------------------------------------------------------------------------- /gifs/02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/02.gif -------------------------------------------------------------------------------- /gifs/03.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/03.gif -------------------------------------------------------------------------------- /gifs/04.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/04.gif -------------------------------------------------------------------------------- /gifs/05.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/05.gif -------------------------------------------------------------------------------- /gifs/06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/06.gif -------------------------------------------------------------------------------- /gifs/07.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/07.gif -------------------------------------------------------------------------------- /gifs/simple01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/simple01.gif -------------------------------------------------------------------------------- /gifs/simple02.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soybin/ckube/d9d1ec2122c51875da62014ae49fc3e0c989b42e/gifs/simple02.gif -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | PREFIX = /usr/local 2 | 3 | ckube: ckube.c 4 | $(CC) ckube.c -o ckube -w -lm -lncursesw -std=c99 5 | 6 | .PHONY: install 7 | install: ckube 8 | mkdir -p $(DESTDIR)$(PREFIX)/bin 9 | sudo cp $< $(DESTDIR)$(PREFIX)/bin/ckube 10 | 11 | .PHONY: uninstall 12 | uninstall: 13 | rm -f $(DESTDIR)$(PREFIX)/bin/ckube 14 | --------------------------------------------------------------------------------