// ----------------------------------------------------------- // https://dataswamp.org/~incal/bad-el/src/bad-sdl3/bad-sdl3.c // ----------------------------------------------------------- int plugin_is_GPL_compatible; #include #include "bad-sdl3.h" #include #include #include #include #include #include #include #include #include #include #define nas __attribute__((unused)) #define lines 32 #define tri 3 #define unset_int -1 #define unset32 UINT32_MAX typedef struct { SDL_Mutex* mtx; char* str; SDL_Surface* sur; size_t new; size_t old; } LineMeta; static SDL_FRect gsr; static SDL_Mutex* cube_mtx = NULL; static SDL_Renderer* rnd = NULL; static SDL_Texture* ptex = NULL; static SDL_Texture* tex = NULL; static SDL_Window* win = NULL; static bool always_draw_mode = true; static bool draw_grid_mode = false; static bool draw_light_mode = true; static int res_w = unset_int; static int res_h = unset_int; static int rnd_w = unset_int; static int rnd_h = unset_int; // --- // log // --- static bool log_enable = true; void logi (const char* s) { if (log_enable) { SDL_ThreadID id = SDL_GetThreadID (NULL); printf ("[%ld] %s\n", id, s); } } // ---- // font // ---- static SDL_Color fg = { 112, 196, 255, 255 }; static TTF_Font* fnt = NULL; static int fnt_sze = 16; static int fnt_w = unset_int; static int fnt_h = unset_int; void font_init () { // SDL3 module if (!TTF_Init()) logi ("DNC TTF_Init"); // font fnt = TTF_OpenFont("/usr/share/fonts/truetype/ocr-a/OCRABold.ttf", fnt_sze); if (!fnt) logi ("DNC TTF_OpenFont"); // size bool manual = true; // todo: get size here if (fnt_w == unset_int) fnt_w = 11; if (fnt_h == unset_int) fnt_h = 21; if (!manual) { // useful in the Elisp part if derived from the actual font printf("[char] %d/%d = %.2f\n", fnt_w, fnt_h, (float) fnt_w / (float) fnt_h); } } void font_quit () { if (fnt) { TTF_CloseFont (fnt); fnt = NULL; } TTF_Quit(); } // ----- // cache // ----- #define cube_side 256 #define cube_size 16777216 static SDL_Texture* cube[cube_size]; static int collisions = 0; static int surfaces_created = 0; static int surfaces_destroyed = 0; static int textures_created = 0; static int textures_destroyed = 0; static int drawn = 0; static int not_drawn = 0; static int timeouts = 0; static int cube_call = 0; static int cube_insert = 0; void cube_init () { cube_mtx = SDL_CreateMutex (); SDL_UnlockMutex (cube_mtx); } void cube_erase () { SDL_LockMutex (cube_mtx); for (uint32_t i = 0; i < cube_size; i++) { if (cube[i]) { SDL_DestroyTexture (cube[i]); textures_destroyed++; cube[i] = NULL; } } SDL_UnlockMutex (cube_mtx); } void cube_quit () { cube_erase (); SDL_DestroyMutex (cube_mtx); cube_mtx = NULL; } // ------ // thread // ------ static LineMeta lines_pipe [lines]; static bool thr_run = false; static SDL_Thread* thr_sur = NULL; static SDL_Thread* thr_rus = NULL; static SDL_Thread* thr_cac = NULL; int thr_surface (void* arg nas) { static __thread int drawn_local = 0; uint32_t idx; uint8_t row; while (thr_run) { for (row = 0; row < lines; row++) { if (SDL_TryLockMutex (lines_pipe[row].mtx)) { // BEG ROW MUTEX if (lines_pipe[row].str) { idx = hash_idx(lines_pipe[row].str) % cube_size; lines_pipe[row].new = idx; if (cube_mtx) { SDL_LockMutex (cube_mtx); // BEG CUBE MUTEX if (!cube[idx]) { if (!lines_pipe[row].sur) { lines_pipe[row].sur = TTF_RenderText_Blended (fnt, lines_pipe[row].str, strlen(lines_pipe[row].str), fg); surfaces_created++; drawn_local++; } } } SDL_UnlockMutex (cube_mtx); // END CUBE MUTEX free (lines_pipe[row].str); lines_pipe[row].str = NULL; } SDL_UnlockMutex (lines_pipe[row].mtx); // END ROW MUTEX } } SDL_Delay(1); } if (drawn_local < 0) { SDL_ThreadID id = SDL_GetThreadID (NULL); printf("[%ld] I made %d surfaces\n", id, drawn_local); } return 0; } int thr_cache (void* arg nas) { int clears = 0; int max_all = 2048; int max_nxt = max_all; while (thr_run) { if ((max_nxt < cube_insert)) { cube_erase (); clears++; max_nxt += max_all; } SDL_Delay (1024); } if (clears < 0) { SDL_ThreadID id = SDL_GetThreadID (NULL); printf("[%ld] I cleared the cache %d times\n", id, clears); } return 0; } void thr_init () { thr_run = true; if (!thr_sur) thr_sur = SDL_CreateThread (thr_surface, "sdl3-surface", NULL); if (!thr_rus) thr_rus = SDL_CreateThread (thr_surface, "sdl3-rusface", NULL); if (!thr_cac) thr_cac = SDL_CreateThread (thr_cache, "sdl3-cache", NULL); } void thr_quit () { thr_run = false; if (thr_sur) { SDL_WaitThread (thr_sur, NULL); thr_sur = NULL; } if (thr_rus) { SDL_WaitThread (thr_rus, NULL); thr_rus = NULL; } if (thr_cac) { SDL_WaitThread (thr_cac, NULL); thr_cac = NULL; } } // ----------- // render text // ----------- uint32_t hash_idx (const char* str) { assert (str); uint8_t dig[SHA256_DIGEST_LENGTH]; SHA256 ((const unsigned char*)str, strlen (str), dig); return (uint32_t) dig[0] << 24 | (uint32_t) dig[1] << 16 | (uint32_t) dig[2] << 8 | (uint32_t) dig[3]; } void store_texture (int i) { // assert assert (0 <= i); assert (i < lines); // lock SDL_LockMutex (cube_mtx); SDL_LockMutex (lines_pipe[i].mtx); if ((lines_pipe[i].new != unset32) && lines_pipe[i].sur) { // collision if (cube[lines_pipe[i].new]) { SDL_DestroyTexture (cube[lines_pipe[i].new]); textures_destroyed++; cube[lines_pipe[i].new] = NULL; collisions++; } // create texture cube[lines_pipe[i].new] = SDL_CreateTextureFromSurface (rnd, lines_pipe[i].sur); SDL_SetTextureBlendMode (cube[lines_pipe[i].new], SDL_BLENDMODE_BLEND); textures_created++; cube_insert++; // destroy surface SDL_DestroySurface (lines_pipe[i].sur); lines_pipe[i].sur = NULL; surfaces_destroyed++; } // unlock SDL_UnlockMutex (lines_pipe[i].mtx); SDL_UnlockMutex (cube_mtx); } void draw_texture (int i) { assert ((0 <= i) && (i < lines)); float tw, th; float nw, nh; int y = fnt_h * i; SDL_GetTextureSize (tex, &tw, &th); SDL_LockMutex (cube_mtx); SDL_LockMutex (lines_pipe[i].mtx); SDL_GetTextureSize (cube[(lines_pipe[i].new)], &nw, &nh); SDL_UnlockMutex (lines_pipe[i].mtx); SDL_UnlockMutex (cube_mtx); // done const SDL_FRect sr = {0, y, round(tw), round(nh) }; const SDL_FRect nr = {0, y, round(nw), round(nh) }; SDL_SetRenderTarget (rnd, tex); SDL_RenderFillRect (rnd, &sr); SDL_LockMutex (cube_mtx); SDL_RenderTexture (rnd, cube[(lines_pipe[i].new)], NULL, &nr); SDL_UnlockMutex (cube_mtx); } void draw_string (const char* str) { int enqd = 0; bool draw = false; char* buff = my_strdup (str); char* save = NULL; char* lne = strtok_r(buff, "\n", &save); for (int i = 0; (i < lines) && lne; i++) { SDL_LockMutex (lines_pipe[i].mtx); if (lines_pipe[i].str) { free (lines_pipe[i].str); lines_pipe[i].str = NULL; } else if (lne) lines_pipe[i].str = my_strdup(lne); SDL_UnlockMutex (lines_pipe[i].mtx); enqd++; cube_call++; lne = strtok_r (NULL, "\n", &save); } // wait for surfaces or refs to cube textures int total = 0; int total_max = 64 * lines; for (int i = 0; ((0 < enqd) && (i < lines) && (total < total_max)); i = (total++) % lines) { if (SDL_TryLockMutex (lines_pipe[i].mtx)) { if (lines_pipe[i].new != unset32) { if (lines_pipe[i].sur) store_texture (i); if (always_draw_mode || (lines_pipe[i].old == unset32) || (lines_pipe[i].old != lines_pipe[i].new)) { draw = true; draw_texture (i); } lines_pipe[i].old = lines_pipe[i].new; lines_pipe[i].new = unset32; enqd--; } SDL_UnlockMutex (lines_pipe[i].mtx); } if (i * 2 == 0) SDL_Delay(1); } if (total_max <= total) timeouts++; if (buff) { free (buff); buff = NULL; } if (draw) drawn++; else not_drawn++; } void draw_screen () { assert (rnd); assert (tex); SDL_SetRenderTarget (rnd, NULL); SDL_RenderTexture (rnd, tex, NULL, &gsr); SDL_RenderPresent (rnd); } // -------------------- // Emacs dynamic module // -------------------- int emacs_module_init (struct emacs_runtime* rnt) { // check for incompatible Emacs binary if (rnt->size < (long int) sizeof (*rnt)) return 1; // check for incompatible module API emacs_env* env = rnt->get_environment (rnt); if (env->size < (long int) sizeof (*env)) return 2; // check for too old Emacs int nas emacs_ver; if ((long int) sizeof (struct emacs_env_31) <= env->size) emacs_ver = 31; else if ((long int) sizeof (struct emacs_env_30) <= env->size) emacs_ver = 30; else if ((long int) sizeof (struct emacs_env_29) <= env->size) emacs_ver = 29; else return 3; // draw_init int nargs = 0; emacs_value draw_init_func = env->make_function (env, nargs, nargs, draw_init, "Init draw.", NULL); emacs_value draw_init_symb = env->intern (env, "draw_init"); emacs_value draw_init_args[] = { draw_init_symb, draw_init_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_init_args); // draw_quit nargs = 0; emacs_value draw_quit_func = env->make_function (env, nargs, nargs, draw_quit, "Quit draw.", NULL); emacs_value draw_quit_symb = env->intern (env, "draw_quit"); emacs_value draw_quit_args[] = { draw_quit_symb, draw_quit_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_quit_args); // draw_frame nargs = 0; emacs_value draw_frame_func = env->make_function (env, nargs, nargs, draw_frame, "Draw frame.", NULL); emacs_value draw_frame_symb = env->intern (env, "draw_frame"); emacs_value draw_frame_args[] = { draw_frame_symb, draw_frame_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_frame_args); // clear_frame nargs = 0; emacs_value clear_frame_func = env->make_function (env, nargs, nargs, clear_frame, "Clear frame.", NULL); emacs_value clear_frame_symb = env->intern (env, "clear_frame"); emacs_value clear_frame_args[] = { clear_frame_symb, clear_frame_func }; env->funcall (env, env->intern (env, "defalias"), 2, clear_frame_args); // sdl_draw_circle nargs = 6; emacs_value sdl_draw_circle_func = env->make_function (env, nargs, nargs, sdl_draw_circle, "SDL draw planet.", NULL); emacs_value sdl_draw_circle_symb = env->intern (env, "sdl_draw_circle"); emacs_value sdl_draw_circle_args[] = { sdl_draw_circle_symb, sdl_draw_circle_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_circle_args); // sdl_draw_triangle nargs = 18; emacs_value sdl_draw_triangle_func = env->make_function (env, nargs, nargs, sdl_draw_triangle, "SDL draw triangle.", NULL); emacs_value sdl_draw_triangle_symb = env->intern (env, "sdl_draw_triangle"); emacs_value sdl_draw_triangle_args[] = { sdl_draw_triangle_symb, sdl_draw_triangle_func }; env->funcall (env, env->intern (env, "defalias"), 2, sdl_draw_triangle_args); // draw_all_toggle nargs = 0; emacs_value draw_all_toggle_func = env->make_function (env, nargs, nargs, draw_all_toggle, "Toggle draw-all mode (disable 'dirty rects' optimization).", NULL); emacs_value draw_all_toggle_symb = env->intern (env, "draw_all_toggle"); emacs_value draw_all_toggle_args[] = { draw_all_toggle_symb, draw_all_toggle_func }; env->funcall (env, env->intern (env, "defalias"), 2, draw_all_toggle_args); // draw_grid_toggle nargs = 0; emacs_value draw_grid_toggle_func = env->make_function (env, nargs, nargs, draw_grid_toggle, "Toggle draw-grid mode.", NULL); emacs_value draw_grid_toggle_symb = env->intern (env, "draw_grid_toggle"); emacs_value draw_grid_toggle_args[] = { draw_grid_toggle_symb, draw_grid_toggle_func }; env->funcall (env, env->intern(env, "defalias"), 2, draw_grid_toggle_args); // done return 0; } emacs_value draw_init (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { // init video_init (); circle_init (); font_init (); cube_init (); thr_init (); // mutexes for (int i = 0; i < lines; i++) { lines_pipe[i].mtx = SDL_CreateMutex (); lines_pipe[i].str = NULL; lines_pipe[i].sur = NULL; lines_pipe[i].new = unset32; lines_pipe[i].old = unset32; } // random unsigned ut = (unsigned) time (NULL); srand (ut); // done return env->intern (env, "nil"); } void mutex_quit () { for (int i = 0; i < lines; i++) { if (lines_pipe[i].mtx) { SDL_DestroyMutex (lines_pipe[i].mtx); lines_pipe[i].mtx = NULL; } } } emacs_value draw_quit (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { assert (env); thr_quit (); cube_quit (); font_quit (); circle_quit (); video_quit (); mutex_quit (); SDL_Quit (); // stats int tot = drawn + not_drawn; if (0 < textures_created) { printf ("%8d textures created\n", textures_created); printf ("%8d -------- destroyed\n", textures_destroyed); } if (0 < surfaces_created) { printf ("%8d surfaces created (%.1f%% of worst-case %d)\n", surfaces_created, (100.0f * (surfaces_created / (1.0f + cube_call))), cube_call); printf ("%8d -------- destroyed\n", surfaces_destroyed); printf ("%8d -------- collisions\n", collisions); } if (0 < tot) { printf ("%8d frames\n", tot); printf ("%8d -------- skipped as identical (%.1f%%)\n", not_drawn, 100.0f * (float) not_drawn / (float) tot); printf ("%8d -------- not drawn as timeout (%.1f%%)\n", timeouts, 100.0f * (float) timeouts / (float) tot); } // done return env->intern(env, "nil"); } // ----- // video // ----- static SDL_DisplayID* display = NULL; void video_init () { SDL_Init(SDL_INIT_VIDEO); // window int num_displays = 1; display = SDL_GetDisplays (&num_displays); const SDL_DisplayMode* dm = SDL_GetCurrentDisplayMode (*display); if (dm) { res_w = dm->w; res_h = dm->h; } else { logi ("DNC SDL_GetCurrentDisplayMode, using fallback values"); res_w = 1280; res_h = 720; } SDL_WindowFlags win_flags = SDL_WINDOW_FULLSCREEN; win = SDL_CreateWindow ("SDL3 draw", res_w, res_h, win_flags); // renderer rnd = SDL_CreateRenderer (win, NULL); SDL_SetRenderDrawBlendMode (rnd, SDL_BLENDMODE_BLEND); SDL_GetCurrentRenderOutputSize (rnd, &rnd_w, &rnd_h); SDL_SetRenderLogicalPresentation (rnd, rnd_w, rnd_h, SDL_LOGICAL_PRESENTATION_LETTERBOX); // tex tex = SDL_CreateTexture (rnd, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, rnd_w, rnd_h); SDL_SetTextureBlendMode (tex, SDL_BLENDMODE_BLEND); textures_created++; // rect gsr.x = 0; gsr.y = 0; gsr.w = rnd_w; gsr.h = rnd_h; // done clear_screen(); } void video_quit () { if (ptex) { SDL_DestroyTexture (ptex); ptex = NULL; textures_destroyed++; } if (tex) { SDL_DestroyTexture (tex); tex = NULL; textures_destroyed++; } if (rnd) { SDL_DestroyRenderer (rnd); rnd = NULL; } if (win) { SDL_DestroyWindow (win); win = NULL; } if (display) { SDL_free (display); display = NULL; } } // ---- // draw // ---- void clear_screen () { SDL_SetRenderDrawColor (rnd, 0, 0, 0, 255); // bg color SDL_SetRenderTarget (rnd, tex); SDL_RenderClear (rnd); } emacs_value clear_frame (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { assert (env); clear_screen(); return env->intern (env, "nil"); } emacs_value draw_frame (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { assert (env); if (draw_grid_mode) { emacs_value ef = env->intern (env, "bad-grid-string"); // Elisp emacs_value es = env->funcall (env, ef, 0, NULL); ptrdiff_t l = 0; env->copy_string_contents (env, es, NULL, &l); if (0 < l) { char* txt_str = NULL; txt_str = malloc (l); env->copy_string_contents (env, es, txt_str, &l); if (txt_str) { draw_string (txt_str); free (txt_str); txt_str = NULL; } } } if (draw_light_mode) draw_light (0, 0, 16); draw_screen (); return env->intern (env, "nil"); } emacs_value sdl_draw_triangle (emacs_env* env, ptrdiff_t nargs, emacs_value* args, void* data nas) { assert (env); assert (nargs == 18); // coords vector v1 = { .x = env->extract_integer (env, args [0]), .y = env->extract_integer (env, args [1]), .z = env->extract_integer (env, args [2]) }; vector v2 = { .x = env->extract_integer (env, args [3]), .y = env->extract_integer (env, args [4]), .z = env->extract_integer (env, args [5]) }; vector v3 = { .x = env->extract_integer (env, args [6]), .y = env->extract_integer (env, args [7]), .z = env->extract_integer (env, args [8]) }; // color SDL_FColor c1 = { .r = env->extract_float (env, args [9]), .g = env->extract_float (env, args [10]), .b = env->extract_float (env, args [11]) }; SDL_FColor c2 = { .r = env->extract_float (env, args [12]), .g = env->extract_float (env, args [13]), .b = env->extract_float (env, args [14]) }; SDL_FColor c3 = { .r = env->extract_float (env, args [15]), .g = env->extract_float (env, args [16]), .b = env->extract_float (env, args [17]) }; // verify coords assert (0 <= fabs (v1.x)); assert (0 <= fabs (v1.y)); assert (0 <= fabs (v1.z)); assert (0 <= fabs (v2.x)); assert (0 <= fabs (v2.y)); assert (0 <= fabs (v2.z)); assert (0 <= fabs (v3.x)); assert (0 <= fabs (v3.y)); assert (0 <= fabs (v3.z)); // verify color assert (0.0f <= c1.r); assert (0.0f <= c1.g); assert (0.0f <= c1.b); assert (0.0f <= c2.r); assert (0.0f <= c2.g); assert (0.0f <= c2.b); assert (0.0f <= c3.r); assert (0.0f <= c3.g); assert (0.0f <= c3.b); // draw draw_triangle (v1, v2, v3, c1, c2, c3); // done return env->intern (env, "nil"); } emacs_value sdl_draw_circle (emacs_env* env, ptrdiff_t nargs, emacs_value* args, void* data nas) { assert (env); assert (nargs == 6); int x = env->extract_integer (env, args[0]); int y = env->extract_integer (env, args[1]); int radius = env->extract_integer (env, args[2]); float red = env->extract_float (env, args[3]); float green = env->extract_float (env, args[4]); float blue = env->extract_float (env, args[5]); SDL_FColor col = { red, green, blue, 1.0f }; draw_circle (x, y, radius, col); return env->intern (env, "nil"); } // ----- // modes // ----- emacs_value draw_all_toggle (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { assert (env); clear_screen(); always_draw_mode = !always_draw_mode; return env->intern (env, "nil"); } emacs_value draw_grid_toggle (emacs_env* env, ptrdiff_t nargs nas, emacs_value* args nas, void* data nas) { assert (env); clear_screen(); draw_grid_mode = !draw_grid_mode; return env->intern (env, "nil"); } // --------- // draw rect // --------- void draw_rect (int x, int y, int w, int h) { const SDL_FRect rect = { .x = x, .y = y, .w = w, .h = h }; SDL_SetRenderDrawColor (rnd, 128, 128, 255, 200); SDL_SetRenderTarget (rnd, tex); SDL_RenderFillRect (rnd, &rect); } void draw_rect_test () { // max size int max_w = unset_int; int max_h = unset_int; SDL_GetCurrentRenderOutputSize (rnd, &max_w, &max_h); if ((max_w != unset_int) && (max_h != unset_int)) { // margin const float part = 1.0f; const float full = 32.0f; int mar_x = (round (max_w * ((1 * part) / full))); int mar_y = (round (max_h * ((4 * part) / full))); // rec int w = max_w - (2 * mar_x); int h = fnt_h * 3; int x = mar_x; int y = max_h - mar_y - h; // done draw_rect(x, y, w, h); } } // ----------------------------------------------------------------------------- // draw light // ----------------------------------------------------------------------------- void draw_light (int cx, int cy, int radius) { int d = 2 * radius; int w = d; int h = d; if (!ptex) { ptex = SDL_CreateTexture(rnd, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, w, h); textures_created++; int pitch = 0; void* pixels = NULL; SDL_LockTexture (ptex, NULL, &pixels, &pitch); int stride = pitch / sizeof(uint32_t); uint32_t* pixel_array = (uint32_t*)pixels; for (int y = cy; y < h; y++) { for (int x = cx; x < w; x++) { float dx = x - radius; float dy = y - radius; float dist = sqrt (dx*dx + dy*dy); float bright = fmax (0.0f, 1.0f - dist/radius); float curve = powf (bright, 1.5f); uint8_t r = (uint8_t)(128 * curve); uint8_t g = (uint8_t)( 96 * curve); uint8_t b = (uint8_t)(255 * curve); uint8_t a = (uint8_t)(212 * curve); pixel_array[y * stride + x] = (a << 24) | (b << 16) | (g << 8) | r; } } SDL_UnlockTexture (ptex); } // draw SDL_SetRenderTarget (rnd, tex); SDL_RenderTexture (rnd, ptex, NULL, NULL); } // ----------- // draw circle // ----------- static SDL_Texture* earth_tex = NULL; void circle_init () { if (rnd && !earth_tex) { earth_tex = IMG_LoadTexture (rnd, "earth.png"); if (earth_tex) textures_created++; } } void circle_quit () { if (earth_tex) { SDL_DestroyTexture (earth_tex); earth_tex = NULL; textures_destroyed++; } } float randf () { const float RAND_MAXF = (float) RAND_MAX; const float rand_rndf = (float) rand(); return (rand_rndf / RAND_MAXF); } void draw_triangle (vector p1, vector p2, vector p3, SDL_FColor c1, SDL_FColor c2, SDL_FColor c3) { const int indices [tri] = { 0, 1, 2 }; const SDL_Vertex vertex [tri] = { { .position = { .x = p1.x, .y = p1.y }, .color = c1 }, { .position = { .x = p2.x, .y = p2.y }, .color = c2 }, { .position = { .x = p3.x, .y = p3.y }, .color = c3 } }; SDL_SetRenderTarget (rnd, tex); SDL_RenderGeometry (rnd, NULL, vertex, tri, indices, tri); SDL_RenderTexture (rnd, tex, NULL, &gsr); } void draw_circle (int cx, int cy, int radius, SDL_FColor col) { const int segs = 96; const int segs_start_stop = segs + 2; SDL_Vertex verts [segs_start_stop]; // int to float const float radiusf = (float) radius; const float cxf = (float) cx; const float cyf = (float) cy; const float segsf = (float) segs; // center verts[0].position = (SDL_FPoint) { .x = cxf, .y = cyf }; verts[0].color = col; if (earth_tex) verts[0].tex_coord = (SDL_FPoint) { .x = 0.5f, .y = 0.5f }; else verts[0].tex_coord = (SDL_FPoint) { .x = 0.0f, .y = 0.0f }; // radial falloff const float bse = 1.0f; const float falloff = 0.5f; // less is more // random jitter const float mod = 0.04f; const float min = bse - mod; const float max = bse + mod; const float dif = max - min; // horizontal modulation const float mod_h = 0.3f; const float min_h = bse - mod_h; const float max_h = bse + mod_h; const float dif_h = max_h - min_h; float i; int j; for (i = 0.0f, j = 1; i <= segs; i++, j++) { // theta and coords float t = (i * 2.0f * pi) / segsf; float x = cxf + radiusf * cosf(t); float y = cyf + radiusf * sinf(t); verts[j].position = (SDL_FPoint) { .x = x, .y = y }; // jitter const float j_red = min + dif * randf(); const float j_grn = min + dif * randf(); const float j_blu = min + dif * randf(); // horzontal const float hori = min_h + dif_h * (1.0f - fabs (sin (t))); // combined verts[j].color = (SDL_FColor) { .r = col.r * falloff * j_red * hori, .g = col.g * falloff * j_grn * hori, .b = col.b * falloff * j_blu * hori, .a = 1.0f }; // texture if (earth_tex) verts[j].tex_coord = (SDL_FPoint) { .x = 0.5f * (1 + cosf (t)), .y = 0.5f * (1 + sinf (t)) }; else verts[j].tex_coord = (SDL_FPoint) { .x = 0.0f, .y = 0.0f }; } // triangles const int tris = segs * 3; int indices [tris]; for (int i = 0, j = 0; i < segs; i++, j = 3 * i) { indices [j] = 0; indices [j + 1] = 1 + i; indices [j + 2] = 2 + i; } // done SDL_SetRenderTarget (rnd, tex); SDL_RenderGeometry (rnd, earth_tex, verts, segs_start_stop, indices, tris); SDL_RenderTexture (rnd, tex, NULL, &gsr); } // ------ // strdup // ------ char* my_strdup (const char* s) { assert (s); if (s) { size_t len = strlen(s) + 1; char* cpy = malloc (len); memcpy(cpy, s, len); return cpy; } return NULL; }