Atlas - woodeneye-008.c
Home / ext / SDL / examples / demo / 02-woodeneye-008 Lines: 1 | Size: 18430 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 * This code is public domain. Feel free to use it for any purpose! 3 */ 4 5#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ 6#include <SDL3/SDL.h> 7#include <SDL3/SDL_main.h> 8 9#define MAP_BOX_SCALE 16 10#define MAP_BOX_EDGES_LEN (12 + MAP_BOX_SCALE * 2) 11#define MAX_PLAYER_COUNT 4 12#define CIRCLE_DRAW_SIDES 32 13#define CIRCLE_DRAW_SIDES_LEN (CIRCLE_DRAW_SIDES + 1) 14 15typedef struct { 16 SDL_MouseID mouse; 17 SDL_KeyboardID keyboard; 18 double pos[3]; 19 double vel[3]; 20 unsigned int yaw; 21 int pitch; 22 float radius, height; 23 unsigned char color[3]; 24 unsigned char wasd; 25} Player; 26 27typedef struct { 28 SDL_Window *window; 29 SDL_Renderer *renderer; 30 int player_count; 31 Player players[MAX_PLAYER_COUNT]; 32 float edges[MAP_BOX_EDGES_LEN][6]; 33} AppState; 34 35static const struct { 36 const char *key; 37 const char *value; 38} extended_metadata[] = { 39 { SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/02-woodeneye-008/" }, 40 { SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" }, 41 { SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" }, 42 { SDL_PROP_APP_METADATA_TYPE_STRING, "game" } 43}; 44 45static int whoseMouse(SDL_MouseID mouse, const Player players[], int players_len) 46{ 47 int i; 48 for (i = 0; i < players_len; i++) { 49 if (players[i].mouse == mouse) return i; 50 } 51 return -1; 52} 53 54static int whoseKeyboard(SDL_KeyboardID keyboard, const Player players[], int players_len) 55{ 56 int i; 57 for (i = 0; i < players_len; i++) { 58 if (players[i].keyboard == keyboard) return i; 59 } 60 return -1; 61} 62 63static void shoot(int shooter, Player players[], int players_len) 64{ 65 int i, j; 66 double x0 = players[shooter].pos[0]; 67 double y0 = players[shooter].pos[1]; 68 double z0 = players[shooter].pos[2]; 69 double bin_rad = SDL_PI_D / 2147483648.0; 70 double yaw_rad = bin_rad * players[shooter].yaw; 71 double pitch_rad = bin_rad * players[shooter].pitch; 72 double cos_yaw = SDL_cos( yaw_rad); 73 double sin_yaw = SDL_sin( yaw_rad); 74 double cos_pitch = SDL_cos(pitch_rad); 75 double sin_pitch = SDL_sin(pitch_rad); 76 double vx = -sin_yaw*cos_pitch; 77 double vy = sin_pitch; 78 double vz = -cos_yaw*cos_pitch; 79 for (i = 0; i < players_len; i++) { 80 if (i == shooter) continue; 81 Player *target = &(players[i]); 82 int hit = 0; 83 for (j = 0; j < 2; j++) { 84 double r = target->radius; 85 double h = target->height; 86 double dx = target->pos[0] - x0; 87 double dy = target->pos[1] - y0 + (j == 0 ? 0 : r - h); 88 double dz = target->pos[2] - z0; 89 double vd = vx*dx + vy*dy + vz*dz; 90 double dd = dx*dx + dy*dy + dz*dz; 91 double vv = vx*vx + vy*vy + vz*vz; 92 double rr = r * r; 93 if (vd < 0) continue; 94 if (vd * vd >= vv * (dd - rr)) hit += 1; 95 } 96 if (hit) { 97 target->pos[0] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256; 98 target->pos[1] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256; 99 target->pos[2] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256; 100 } 101 } 102} 103 104static void update(Player *players, int players_len, Uint64 dt_ns) 105{ 106 int i; 107 for (i = 0; i < players_len; i++) { 108 Player *player = &players[i]; 109 double rate = 6.0; 110 double time = (double)dt_ns * 1e-9; 111 double drag = SDL_exp(-time * rate); 112 double diff = 1.0 - drag; 113 double mult = 60.0; 114 double grav = 25.0; 115 double yaw = (double)player->yaw; 116 double rad = yaw * SDL_PI_D / 2147483648.0; 117 double cos = SDL_cos(rad); 118 double sin = SDL_sin(rad); 119 unsigned char wasd = player->wasd; 120 double dirX = (wasd & 8 ? 1.0 : 0.0) - (wasd & 2 ? 1.0 : 0.0); 121 double dirZ = (wasd & 4 ? 1.0 : 0.0) - (wasd & 1 ? 1.0 : 0.0); 122 double norm = dirX * dirX + dirZ * dirZ; 123 double accX = mult * (norm == 0 ? 0 : ( cos*dirX + sin*dirZ) / SDL_sqrt(norm)); 124 double accZ = mult * (norm == 0 ? 0 : (-sin*dirX + cos*dirZ) / SDL_sqrt(norm)); 125 double velX = player->vel[0]; 126 double velY = player->vel[1]; 127 double velZ = player->vel[2]; 128 player->vel[0] -= velX * diff; 129 player->vel[1] -= grav * time; 130 player->vel[2] -= velZ * diff; 131 player->vel[0] += diff * accX / rate; 132 player->vel[2] += diff * accZ / rate; 133 player->pos[0] += (time - diff/rate) * accX / rate + diff * velX / rate; 134 player->pos[1] += -0.5 * grav * time * time + velY * time; 135 player->pos[2] += (time - diff/rate) * accZ / rate + diff * velZ / rate; 136 double scale = (double)MAP_BOX_SCALE; 137 double bound = scale - player->radius; 138 double posX = SDL_max(SDL_min(bound, player->pos[0]), -bound); 139 double posY = SDL_max(SDL_min(bound, player->pos[1]), player->height - scale); 140 double posZ = SDL_max(SDL_min(bound, player->pos[2]), -bound); 141 if (player->pos[0] != posX) player->vel[0] = 0; 142 if (player->pos[1] != posY) player->vel[1] = (wasd & 16) ? 8.4375 : 0; 143 if (player->pos[2] != posZ) player->vel[2] = 0; 144 player->pos[0] = posX; 145 player->pos[1] = posY; 146 player->pos[2] = posZ; 147 } 148} 149 150static void drawCircle(SDL_Renderer *renderer, float r, float x, float y) 151{ 152 float ang; 153 SDL_FPoint points[CIRCLE_DRAW_SIDES_LEN]; 154 int i; 155 for (i = 0; i < CIRCLE_DRAW_SIDES_LEN; i++) { 156 ang = 2.0f * SDL_PI_F * (float)i / (float)CIRCLE_DRAW_SIDES; 157 points[i].x = x + r * SDL_cosf(ang); 158 points[i].y = y + r * SDL_sinf(ang); 159 } 160 SDL_RenderLines(renderer, (const SDL_FPoint*)&points, CIRCLE_DRAW_SIDES_LEN); 161} 162 163static void drawClippedSegment( 164 SDL_Renderer *renderer, 165 float ax, float ay, float az, 166 float bx, float by, float bz, 167 float x, float y, float z, float w) 168{ 169 if (az >= -w && bz >= -w) return; 170 float dx = ax - bx; 171 float dy = ay - by; 172 if (az > -w) { 173 float t = (-w - bz) / (az - bz); 174 ax = bx + dx * t; 175 ay = by + dy * t; 176 az = -w; 177 } else if (bz > -w) { 178 float t = (-w - az) / (bz - az); 179 bx = ax - dx * t; 180 by = ay - dy * t; 181 bz = -w; 182 } 183 ax = -z * ax / az; 184 ay = -z * ay / az; 185 bx = -z * bx / bz; 186 by = -z * by / bz; 187 SDL_RenderLine(renderer, x + ax, y - ay, x + bx, y - by); 188} 189 190static char debug_string[32]; 191static void draw(SDL_Renderer *renderer, const float (*edges)[6], const Player players[], int players_len) 192{ 193 int w, h, i, j, k; 194 if (!SDL_GetRenderOutputSize(renderer, &w, &h)) { 195 return; 196 } 197 SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); 198 SDL_RenderClear(renderer); 199 if (players_len > 0) { 200 float wf = (float)w; 201 float hf = (float)h; 202 int part_hor = players_len > 2 ? 2 : 1; 203 int part_ver = players_len > 1 ? 2 : 1; 204 float size_hor = wf / ((float)part_hor); 205 float size_ver = hf / ((float)part_ver); 206 for (i = 0; i < players_len; i++) { 207 const Player *player = &players[i]; 208 float mod_x = (float)(i % part_hor); 209 float mod_y = (float)(i / part_hor); 210 float hor_origin = (mod_x + 0.5f) * size_hor; 211 float ver_origin = (mod_y + 0.5f) * size_ver; 212 float cam_origin = (float)(0.5 * SDL_sqrt(size_hor * size_hor + size_ver * size_ver)); 213 float hor_offset = mod_x * size_hor; 214 float ver_offset = mod_y * size_ver; 215 SDL_Rect rect; 216 rect.x = (int)hor_offset; 217 rect.y = (int)ver_offset; 218 rect.w = (int)size_hor; 219 rect.h = (int)size_ver; 220 SDL_SetRenderClipRect(renderer, &rect); 221 double x0 = player->pos[0]; 222 double y0 = player->pos[1]; 223 double z0 = player->pos[2]; 224 double bin_rad = SDL_PI_D / 2147483648.0; 225 double yaw_rad = bin_rad * player->yaw; 226 double pitch_rad = bin_rad * player->pitch; 227 double cos_yaw = SDL_cos( yaw_rad); 228 double sin_yaw = SDL_sin( yaw_rad); 229 double cos_pitch = SDL_cos(pitch_rad); 230 double sin_pitch = SDL_sin(pitch_rad); 231 double mat[9] = { 232 cos_yaw , 0, -sin_yaw , 233 sin_yaw*sin_pitch, cos_pitch, cos_yaw*sin_pitch, 234 sin_yaw*cos_pitch, -sin_pitch, cos_yaw*cos_pitch 235 }; 236 SDL_SetRenderDrawColor(renderer, 64, 64, 64, 255); 237 for (k = 0; k < MAP_BOX_EDGES_LEN; k++) { 238 const float *line = edges[k]; 239 float ax = (float)(mat[0] * (line[0] - x0) + mat[1] * (line[1] - y0) + mat[2] * (line[2] - z0)); 240 float ay = (float)(mat[3] * (line[0] - x0) + mat[4] * (line[1] - y0) + mat[5] * (line[2] - z0)); 241 float az = (float)(mat[6] * (line[0] - x0) + mat[7] * (line[1] - y0) + mat[8] * (line[2] - z0)); 242 float bx = (float)(mat[0] * (line[3] - x0) + mat[1] * (line[4] - y0) + mat[2] * (line[5] - z0)); 243 float by = (float)(mat[3] * (line[3] - x0) + mat[4] * (line[4] - y0) + mat[5] * (line[5] - z0)); 244 float bz = (float)(mat[6] * (line[3] - x0) + mat[7] * (line[4] - y0) + mat[8] * (line[5] - z0)); 245 drawClippedSegment(renderer, ax, ay, az, bx, by, bz, hor_origin, ver_origin, cam_origin, 1); 246 } 247 for (j = 0; j < players_len; j++) { 248 if (i == j) continue; 249 const Player *target = &players[j]; 250 SDL_SetRenderDrawColor(renderer, target->color[0], target->color[1], target->color[2], 255); 251 for (k = 0; k < 2; k++) { 252 double rx = target->pos[0] - player->pos[0]; 253 double ry = target->pos[1] - player->pos[1] + (target->radius - target->height) * (float)k; 254 double rz = target->pos[2] - player->pos[2]; 255 double dx = mat[0] * rx + mat[1] * ry + mat[2] * rz; 256 double dy = mat[3] * rx + mat[4] * ry + mat[5] * rz; 257 double dz = mat[6] * rx + mat[7] * ry + mat[8] * rz; 258 double r_eff = target->radius * cam_origin / dz; 259 if (!(dz < 0)) continue; 260 drawCircle(renderer, (float)(r_eff), (float)(hor_origin - cam_origin*dx/dz), (float)(ver_origin + cam_origin*dy/dz)); 261 } 262 } 263 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); 264 SDL_RenderLine(renderer, hor_origin, ver_origin-10, hor_origin, ver_origin+10); 265 SDL_RenderLine(renderer, hor_origin-10, ver_origin, hor_origin+10, ver_origin); 266 } 267 } 268 SDL_SetRenderClipRect(renderer, NULL); 269 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); 270 SDL_RenderDebugText(renderer, 0, 0, debug_string); 271 SDL_RenderPresent(renderer); 272} 273 274static void initPlayers(Player *players, int len) 275{ 276 int i; 277 for (i = 0; i < len; i++) { 278 players[i].pos[0] = 8.0 * (i & 1 ? -1.0 : 1.0); 279 players[i].pos[1] = 0; 280 players[i].pos[2] = 8.0 * (i & 1 ? -1.0 : 1.0) * (i & 2 ? -1.0 : 1.0); 281 players[i].vel[0] = 0; 282 players[i].vel[1] = 0; 283 players[i].vel[2] = 0; 284 players[i].yaw = 0x20000000 + (i & 1 ? 0x80000000 : 0) + (i & 2 ? 0x40000000 : 0); 285 players[i].pitch = -0x08000000; 286 players[i].radius = 0.5f; 287 players[i].height = 1.5f; 288 players[i].wasd = 0; 289 players[i].mouse = 0; 290 players[i].keyboard = 0; 291 players[i].color[0] = (1 << (i / 2)) & 2 ? 0 : 0xff; 292 players[i].color[1] = (1 << (i / 2)) & 1 ? 0 : 0xff; 293 players[i].color[2] = (1 << (i / 2)) & 4 ? 0 : 0xff; 294 players[i].color[0] = (i & 1) ? players[i].color[0] : ~players[i].color[0]; 295 players[i].color[1] = (i & 1) ? players[i].color[1] : ~players[i].color[1]; 296 players[i].color[2] = (i & 1) ? players[i].color[2] : ~players[i].color[2]; 297 } 298} 299 300static void initEdges(int scale, float (*edges)[6], int edges_len) 301{ 302 int i, j; 303 const float r = (float)scale; 304 const int map[24] = { 305 0,1 , 1,3 , 3,2 , 2,0 , 306 7,6 , 6,4 , 4,5 , 5,7 , 307 6,2 , 3,7 , 0,4 , 5,1 308 }; 309 for(i = 0; i < 12; i++) { 310 for (j = 0; j < 3; j++) { 311 edges[i][j+0] = (map[i*2+0] & (1 << j) ? r : -r); 312 edges[i][j+3] = (map[i*2+1] & (1 << j) ? r : -r); 313 } 314 } 315 for(i = 0; i < scale; i++) { 316 float d = (float)(i * 2); 317 for (j = 0; j < 2; j++) { 318 edges[i+12][3*j+0] = j ? r : -r; 319 edges[i+12][3*j+1] = -r; 320 edges[i+12][3*j+2] = d-r; 321 edges[i+12+scale][3*j+0] = d-r; 322 edges[i+12+scale][3*j+1] = -r; 323 edges[i+12+scale][3*j+2] = j ? r : -r; 324 } 325 } 326} 327 328SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) 329{ 330 if (!SDL_SetAppMetadata("Example splitscreen shooter game", "1.0", "com.example.woodeneye-008")) { 331 return SDL_APP_FAILURE; 332 } 333 int i; 334 for (i = 0; i < SDL_arraysize(extended_metadata); i++) { 335 if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) { 336 return SDL_APP_FAILURE; 337 } 338 } 339 340 AppState *as = SDL_calloc(1, sizeof(AppState)); 341 if (!as) { 342 return SDL_APP_FAILURE; 343 } else { 344 *appstate = as; 345 } 346 347 if (!SDL_Init(SDL_INIT_VIDEO)) { 348 return SDL_APP_FAILURE; 349 } 350 if (!SDL_CreateWindowAndRenderer("examples/demo/woodeneye-008", 640, 480, SDL_WINDOW_RESIZABLE, &as->window, &as->renderer)) { 351 return SDL_APP_FAILURE; 352 } 353 354 as->player_count = 1; 355 initPlayers(as->players, MAX_PLAYER_COUNT); 356 initEdges(MAP_BOX_SCALE, as->edges, MAP_BOX_EDGES_LEN); 357 debug_string[0] = 0; 358 359 SDL_SetRenderVSync(as->renderer, false); 360 SDL_SetWindowRelativeMouseMode(as->window, true); 361 SDL_SetHintWithPriority(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1", SDL_HINT_OVERRIDE); 362 return SDL_APP_CONTINUE; 363} 364 365SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) 366{ 367 AppState *as = appstate; 368 Player *players = as->players; 369 int player_count = as->player_count; 370 int i; 371 switch (event->type) { 372 case SDL_EVENT_QUIT: 373 return SDL_APP_SUCCESS; 374 break; 375 case SDL_EVENT_MOUSE_REMOVED: 376 for (i = 0; i < player_count; i++) { 377 if (players[i].mouse == event->mdevice.which) { 378 players[i].mouse = 0; 379 } 380 } 381 break; 382 case SDL_EVENT_KEYBOARD_REMOVED: 383 for (i = 0; i < player_count; i++) { 384 if (players[i].keyboard == event->kdevice.which) { 385 players[i].keyboard = 0; 386 } 387 } 388 break; 389 case SDL_EVENT_MOUSE_MOTION: { 390 SDL_MouseID id = event->motion.which; 391 int index = whoseMouse(id, players, player_count); 392 if (index >= 0) { 393 players[index].yaw -= ((int)event->motion.xrel) * 0x00080000; 394 players[index].pitch = SDL_max(-0x40000000, SDL_min(0x40000000, players[index].pitch - ((int)event->motion.yrel) * 0x00080000)); 395 } else if (id) { 396 for (i = 0; i < MAX_PLAYER_COUNT; i++) { 397 if (players[i].mouse == 0) { 398 players[i].mouse = id; 399 as->player_count = SDL_max(as->player_count, i + 1); 400 break; 401 } 402 } 403 } 404 break; 405 } 406 case SDL_EVENT_MOUSE_BUTTON_DOWN: { 407 SDL_MouseID id = event->button.which; 408 int index = whoseMouse(id, players, player_count); 409 if (index >= 0) { 410 shoot(index, players, player_count); 411 } 412 break; 413 } 414 case SDL_EVENT_KEY_DOWN: { 415 SDL_Keycode sym = event->key.key; 416 SDL_KeyboardID id = event->key.which; 417 int index = whoseKeyboard(id, players, player_count); 418 if (index >= 0) { 419 if (sym == SDLK_W) players[index].wasd |= 1; 420 if (sym == SDLK_A) players[index].wasd |= 2; 421 if (sym == SDLK_S) players[index].wasd |= 4; 422 if (sym == SDLK_D) players[index].wasd |= 8; 423 if (sym == SDLK_SPACE) players[index].wasd |= 16; 424 } else if (id) { 425 for (i = 0; i < MAX_PLAYER_COUNT; i++) { 426 if (players[i].keyboard == 0) { 427 players[i].keyboard = id; 428 as->player_count = SDL_max(as->player_count, i + 1); 429 break; 430 } 431 } 432 } 433 break; 434 } 435 case SDL_EVENT_KEY_UP: { 436 SDL_Keycode sym = event->key.key; 437 SDL_KeyboardID id = event->key.which; 438 if (sym == SDLK_ESCAPE) return SDL_APP_SUCCESS; 439 int index = whoseKeyboard(id, players, player_count); 440 if (index >= 0) { 441 if (sym == SDLK_W) players[index].wasd &= 30; 442 if (sym == SDLK_A) players[index].wasd &= 29; 443 if (sym == SDLK_S) players[index].wasd &= 27; 444 if (sym == SDLK_D) players[index].wasd &= 23; 445 if (sym == SDLK_SPACE) players[index].wasd &= 15; 446 } 447 break; 448 } 449 } 450 return SDL_APP_CONTINUE; 451} 452 453SDL_AppResult SDL_AppIterate(void *appstate) 454{ 455 AppState *as = appstate; 456 static Uint64 accu = 0; 457 static Uint64 last = 0; 458 static Uint64 past = 0; 459 Uint64 now = SDL_GetTicksNS(); 460 Uint64 dt_ns = now - past; 461 update(as->players, as->player_count, dt_ns); 462 draw(as->renderer, (const float (*)[6])as->edges, as->players, as->player_count); 463 if (now - last > 999999999) { 464 last = now; 465 SDL_snprintf(debug_string, sizeof(debug_string), "%" SDL_PRIu64 " fps", accu); 466 accu = 0; 467 } 468 past = now; 469 accu += 1; 470 Uint64 elapsed = SDL_GetTicksNS() - now; 471 if (elapsed < 999999) { 472 SDL_DelayNS(999999 - elapsed); 473 } 474 return SDL_APP_CONTINUE; 475} 476 477void SDL_AppQuit(void *appstate, SDL_AppResult result) 478{ 479 SDL_free(appstate); // just free the memory, SDL will clean up the window/renderer for us. 480}[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.