├── 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 | 
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 |
--------------------------------------------------------------------------------