Author SHA1 Message Date
odweta ad6536bdab add rotating 3d cube 2026-10-14 02:52:30 +02:00
odweta 403ddc132d Merge pull request 'add better colors for 2048' (#32) from fix/add-2048-tile-colors into dev
Reviewed-on: #32
2026-10-10 14:47:22 +02:00
odweta f9734c3c0a add better colors for 2048 2026-10-10 14:46:08 +02:00
6 changed files with 388 additions and 11 deletions
+6 -2
View File
@@ -9,6 +9,10 @@ typedef enum {
OVERDRAW OVERDRAW
} framebuffer_e; } framebuffer_e;
typedef struct {
double x, y;
} v2;
typedef struct { typedef struct {
SDL_Event event; SDL_Event event;
bool is_running; bool is_running;
@@ -17,9 +21,9 @@ typedef struct {
SDL_Rect dirty_rect; SDL_Rect dirty_rect;
uint16_t width, height; uint16_t width, height;
// base layer ~ layer 0 // base layer ~ layer 0
uint32_t *framebuffer_base; uint32_t *framebuffer_base;
// layer on top of that ~ layer 1 // layer on top of that ~ layer 1
uint32_t *framebuffer_overdraw; uint32_t *framebuffer_overdraw;
} game_state_t; } game_state_t;
extern game_state_t *game_state; extern game_state_t *game_state;
+21 -4
View File
@@ -4,16 +4,33 @@
#include <stdint.h> #include <stdint.h>
void void
put_pixel( put_pixel(
framebuffer_e framebuffer, framebuffer_e framebuffer,
unsigned int x, unsigned int x,
unsigned int y, unsigned int y,
uint32_t color uint32_t color
); );
void void
clear_framebuffer( clear_framebuffer(
framebuffer_e framebuffer, framebuffer_e framebuffer,
uint32_t color uint32_t color
); );
void
put_line(
v2 a,
v2 b,
framebuffer_e framebuffer,
uint32_t color
);
void
put_triangle(
v2 a,
v2 b,
v2 c,
framebuffer_e framebuffer,
uint32_t color
);
+87 -4
View File
@@ -6,6 +6,8 @@
#include "internal/util/error.h" #include "internal/util/error.h"
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <math.h>
// helper function to return the correct pointer to a framebuffer for drawing selection // helper function to return the correct pointer to a framebuffer for drawing selection
uint32_t* uint32_t*
@@ -23,11 +25,11 @@ get_framebuffer(
} }
// alter a pixel at an imaginary x & y coordinates // alter a pixel at an imaginary x & y coordinates
void void
put_pixel( put_pixel(
framebuffer_e framebuffer, framebuffer_e framebuffer,
unsigned int x, unsigned int x,
unsigned int y, unsigned int y,
uint32_t color uint32_t color
) { ) {
uint32_t *current_framebuffer = get_framebuffer(framebuffer); uint32_t *current_framebuffer = get_framebuffer(framebuffer);
@@ -55,7 +57,7 @@ clear_framebuffer(
uint32_t color uint32_t color
) { ) {
uint32_t *current_framebuffer = get_framebuffer(framebuffer); uint32_t *current_framebuffer = get_framebuffer(framebuffer);
for (size_t i = 0; for (size_t i = 0;
i < (size_t)game_state->width * game_state->height; i < (size_t)game_state->width * game_state->height;
++i) { ++i) {
@@ -69,3 +71,84 @@ clear_framebuffer(
game_state->height game_state->height
); );
} }
void
normalize_v2(
v2 input,
v2 *output
) {
double min = (input.x > input.y) ? input.y : input.x;
for (int i = min; i > 1; i--) {
if ((int)input.x % (int)min == 0 && (int)input.y % (int)min == 0) {
input.x /= min;
input.y /= min;
break;
}
}
output->x = input.x;
output->y = input.y;
}
// subtract vector b from vector a
void
sub_v2(
v2 a,
v2 b,
v2 *dest
) {
dest->x = b.x - a.x;
dest->y = b.y - a.y;
}
// draw a line between two points with a given color
void
put_line(
v2 a, v2 b,
framebuffer_e fb,
uint32_t color
) {
int x0 = (int)round(a.x);
int y0 = (int)round(a.y);
int x1 = (int)round(b.x);
int y1 = (int)round(b.y);
int dx = abs(x1 - x0);
int sx = x0 < x1 ? 1 : -1;
int dy = -abs(y1 - y0);
int sy = y0 < y1 ? 1 : -1;
int err = dx + dy;
while (1) {
put_pixel(fb, x0, y0, color);
if (x0 == x1 && y0 == y1)
break;
int e2 = 2 * err;
if (e2 >= dy) {
err += dy;
x0 += sx;
}
if (e2 <= dx) {
err += dx;
y0 += sy;
}
}
}
// draw a frame of a triangle with a given color
void
put_triangle(
v2 a,
v2 b,
v2 c,
framebuffer_e framebuffer,
uint32_t color
) {
put_line(a, b, framebuffer, color);
put_line(b, c, framebuffer, color);
put_line(a, c, framebuffer, color);
}
+38 -1
View File
@@ -10,6 +10,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <math.h>
#define SIZE 4 #define SIZE 4
#define MOVE_DURATION 0.12f #define MOVE_DURATION 0.12f
@@ -74,6 +75,42 @@ colors(uint32_t number)
} }
} }
/* Maps positive values logarithmically: powers of two get evenly spaced hues.
Returns 0xRRGGBB. Values outside [min_value, max_value] are clamped. */
uint32_t
color_for_value(double value, double min_value, double max_value)
{
if (min_value <= 0.0 || max_value <= min_value || value <= 0.0)
return 0xFFFFFF;
if (value < min_value) value = min_value;
if (value > max_value) value = max_value;
double t = (log2(value) - log2(min_value)) /
(log2(max_value) - log2(min_value));
t = sqrt(t); /* accelerates color changes near the low end */
/* Sweep hue from blue to red in HSV; full saturation and brightness. */
// double h = (1.0 - t) * 240.0;
double exponent = log2(value / min_value); /* 0 at min_value, 1 per doubling */
double h = fmod(20.0 + exponent * 45.0, 360.0);
double c = 1.0;
double x = 1.0 - fabs(fmod(h / 60.0, 2.0) - 1.0);
double r, g, b;
if (h < 60.0) { r = c; g = x; b = 0.0; }
else if (h < 120.0) { r = x; g = c; b = 0.0; }
else if (h < 180.0) { r = 0.0; g = c; b = x; }
else { r = 0.0; g = x; b = c; }
return ((uint32_t)(r * 255.0) << 16) |
((uint32_t)(g * 255.0) << 8) |
(uint32_t)(b * 255.0);
}
/* /*
* Return the board coordinate for position p in a line, where p=0 is * Return the board coordinate for position p in a line, where p=0 is
* the side toward which tiles move. * the side toward which tiles move.
@@ -323,7 +360,7 @@ draw_tile_rect(uint32_t value, float x, float y, float w, float h)
uint32_t iy = (uint32_t)y; uint32_t iy = (uint32_t)y;
uint32_t iw = (uint32_t)w; uint32_t iw = (uint32_t)w;
uint32_t ih = (uint32_t)h; uint32_t ih = (uint32_t)h;
uint32_t color = colors(value); uint32_t color = color_for_value((double)value, 2, 131072);
put_rect(OVERDRAW, ix, iy, iw, ih, color); put_rect(OVERDRAW, ix, iy, iw, ih, color);
+231
View File
@@ -0,0 +1,231 @@
#include <dangerzone/engine.h>
#include <dangerzone/render/misc.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// a point in 3D space
typedef struct
{
double x, y, z;
} v3;
// a triangle with 3 points in 3D space
typedef struct
{
v3 p[3];
} tri;
// a grouping of triangles - can desribe any 3D object
typedef struct
{
tri *tris;
} mesh;
typedef struct
{
float m[4][4];
} mat4x4;
#define PI 3.1415926535
void multiply_matrix_vector(v3 input, v3 *output, mat4x4 m)
{
output->x = input.x * m.m[0][0] + input.y * m.m[1][0] + input.z * m.m[2][0] + m.m[3][0];
output->y = input.x * m.m[0][1] + input.y * m.m[1][1] + input.z * m.m[2][1] + m.m[3][1];
output->z = input.x * m.m[0][2] + input.y * m.m[1][2] + input.z * m.m[2][2] + m.m[3][2];
double w = input.x * m.m[0][3] + input.y * m.m[1][3] + input.z * m.m[2][3] + m.m[3][3];
if (w != 0)
{
output->x /= w;
output->y /= w;
output->z /= w;
}
}
#define ASSERT(_e) do { if (!(_e)) { fprintf(stderr, "assert failed for %s\n", #_e); exit(1); } } while (0)
struct {
mesh m;
mat4x4 mproj;
double near, far, fov;
unsigned int screen_width, screen_height;
} state;
// a unit-sized cube mesh defined with triangles
#define CUBE_TRI_AMOUNT 12
tri cube_mesh[CUBE_TRI_AMOUNT] =
{
// south
{{ {0,0,0}, {0,1,0}, {1,1,0} }},
{{ {0,0,0}, {1,1,0}, {1,0,0} }},
// west
{{ {1,0,0}, {1,1,0}, {1,1,1} }},
{{ {1,0,0}, {1,1,1}, {1,0,1} }},
// north
{{ {1,0,1}, {1,1,1}, {0,1,1} }},
{{ {1,0,1}, {0,1,1}, {0,0,1} }},
// east
{{ {0,0,1}, {0,1,1}, {0,1,0} }},
{{ {0,0,1}, {0,1,0}, {0,0,0} }},
// top
{{ {0,1,0}, {0,1,1}, {1,1,1} }},
{{ {0,1,0}, {1,1,1}, {1,1,0} }},
// bottom
{{ {0,0,1}, {0,0,0}, {1,0,0} }},
{{ {0,0,1}, {1,0,0}, {1,0,1} }},
};
void
game_init()
{
state.m.tris = cube_mesh;
memset(&state.mproj, 0, sizeof(state.mproj));
state.near = 0.1;
state.far = 1000000.0;
state.fov = 90.0;
state.screen_width = 500;
state.screen_height = 500;
// aspect ratio
double a = (float)state.screen_height / (float)state.screen_width;
// fov tangent
double fov_rad = 1.0 / tanf(state.fov * 0.5 / 180.0 * PI);
state.mproj.m[0][0] = a * fov_rad;
state.mproj.m[1][1] = fov_rad;
state.mproj.m[2][2] = state.far / (state.far - state.near);
state.mproj.m[3][2] = (-state.far * state.near) / (state.far - state.near);
state.mproj.m[2][3] = 1.0;
}
void
client_poll_events()
{
SDL_Event ev = game_state->event;
if (ev.type == SDL_EVENT_QUIT) {
game_state->is_running = false;
}
if (ev.type == SDL_EVENT_KEY_DOWN) {
switch (ev.key.key) {
case SDLK_Q:
game_state->is_running = false;
break;
default:
break;
}
}
}
void
client_update_framebuffer()
{
clear_framebuffer(BASE, 0x000000);
clear_framebuffer(OVERDRAW, 0x000000);
mat4x4 mrotz = { 0 }, mrotx = { 0 };
double theta = SDL_GetTicks() / 1000.0;
// Rotation Z
mrotz.m[0][0] = cosf(theta);
mrotz.m[0][1] = sinf(theta);
mrotz.m[1][0] = -sinf(theta);
mrotz.m[1][1] = cosf(theta);
mrotz.m[2][2] = 1;
mrotz.m[3][3] = 1;
// Rotation X
mrotx.m[0][0] = 1;
mrotx.m[1][1] = cosf(theta * 0.5f);
mrotx.m[1][2] = sinf(theta * 0.5f);
mrotx.m[2][1] = -sinf(theta * 0.5f);
mrotx.m[2][2] = cosf(theta * 0.5f);
mrotx.m[3][3] = 1;
for (int i = 0; i < CUBE_TRI_AMOUNT; i++)
{
// project from 3D to 2D
tri triangle = state.m.tris[i];
for (int k = 0; k < 3; k++) {
triangle.p[k].x -= 0.5;
triangle.p[k].y -= 0.5;
triangle.p[k].z -= 0.5;
}
tri projected, translated, rotatedz, rotatedzx;
multiply_matrix_vector(triangle.p[0], &rotatedz.p[0], mrotz);
multiply_matrix_vector(triangle.p[1], &rotatedz.p[1], mrotz);
multiply_matrix_vector(triangle.p[2], &rotatedz.p[2], mrotz);
multiply_matrix_vector(rotatedz.p[0], &rotatedzx.p[0], mrotx);
multiply_matrix_vector(rotatedz.p[1], &rotatedzx.p[1], mrotx);
multiply_matrix_vector(rotatedz.p[2], &rotatedzx.p[2], mrotx);
translated = rotatedzx;
translated.p[0].z = rotatedzx.p[0].z + 3.0;
translated.p[1].z = rotatedzx.p[1].z + 3.0;
translated.p[2].z = rotatedzx.p[2].z + 3.0;
multiply_matrix_vector(translated.p[0], &projected.p[0], state.mproj);
multiply_matrix_vector(translated.p[1], &projected.p[1], state.mproj);
multiply_matrix_vector(translated.p[2], &projected.p[2], state.mproj);
// scale into view
projected.p[0].x += 1.0;
projected.p[0].y += 1.0;
projected.p[1].x += 1.0;
projected.p[1].y += 1.0;
projected.p[2].x += 1.0;
projected.p[2].y += 1.0;
projected.p[0].x *= 0.5 * state.screen_width;
projected.p[0].y *= 0.5 * state.screen_height;
projected.p[1].x *= 0.5 * state.screen_width;
projected.p[1].y *= 0.5 * state.screen_height;
projected.p[2].x *= 0.5 * state.screen_width;
projected.p[2].y *= 0.5 * state.screen_height;
put_triangle(
(v2){.x=projected.p[0].x, .y=projected.p[0].y},
(v2){.x=projected.p[1].x, .y=projected.p[1].y},
(v2){.x=projected.p[2].x, .y=projected.p[2].y},
OVERDRAW, 0xFFFFFF
);
}
}
int main()
{
game_init();
if (engine_init(
state.screen_width, state.screen_height, "3D demo",
client_poll_events, client_update_framebuffer
)) {
engine_start();
engine_stop();
return 0;
} else {
return 1;
}
}
+5
View File
@@ -3,6 +3,11 @@
add_executable(game_2048 2048.c) add_executable(game_2048 2048.c)
target_link_libraries(game_2048 PRIVATE dangerzone_engine) target_link_libraries(game_2048 PRIVATE dangerzone_engine)
target_link_libraries(game_2048 PRIVATE m)
add_executable(game_3d 3d.c)
target_link_libraries(game_3d PRIVATE dangerzone_engine)
target_link_libraries(game_3d PRIVATE m)
#add_executable(game_window window.c) #add_executable(game_window window.c)
#target_link_libraries(game_window PRIVATE dangerzone_engine) #target_link_libraries(game_window PRIVATE dangerzone_engine)