|
19 | 19 | #include "font_data.h" |
20 | 20 | #include "image_data.h" |
21 | 21 | #include "logo_data.h" |
| 22 | +#include "infix_data.h" |
| 23 | +#include "wires_data.h" |
22 | 24 |
|
23 | 25 | /* Music data will be included when available */ |
24 | 26 | #ifdef HAVE_MUSIC |
@@ -65,6 +67,10 @@ typedef struct { |
65 | 67 | SDL_Texture *jack_texture; |
66 | 68 | SDL_Surface *logo_surface; |
67 | 69 | SDL_Texture *logo_texture; |
| 70 | + SDL_Surface *infix_surface; |
| 71 | + SDL_Texture *infix_texture; |
| 72 | + SDL_Surface *wires_surface; |
| 73 | + SDL_Texture *wires_texture; |
68 | 74 | int current_scene; |
69 | 75 | int current_scene_index; /* Index into scene_list */ |
70 | 76 | int fixed_scene; |
@@ -288,6 +294,207 @@ void render_starfield(DemoContext *ctx) |
288 | 294 | } |
289 | 295 | } |
290 | 296 |
|
| 297 | + /* Burning Infix logo in upper left corner */ |
| 298 | + if (ctx->infix_texture && ctx->infix_surface) { |
| 299 | + /* Scale logo to 40% */ |
| 300 | + int orig_w = ctx->infix_surface->w; |
| 301 | + int orig_h = ctx->infix_surface->h; |
| 302 | + int logo_w = (int)(orig_w * 0.4f); |
| 303 | + int logo_h = (int)(orig_h * 0.4f); |
| 304 | + int logo_x = 20; /* Upper left corner */ |
| 305 | + int logo_y = 20; |
| 306 | + |
| 307 | + /* Create fire effect buffer matching logo dimensions */ |
| 308 | + static Uint32 fire_buffer[512 * 256]; /* Fire buffer */ |
| 309 | + static int fire_frame_skip = 0; |
| 310 | + int fire_w = logo_w; |
| 311 | + int fire_h = logo_h; |
| 312 | + |
| 313 | + /* Update fire every 5th frame to slow it down */ |
| 314 | + fire_frame_skip++; |
| 315 | + if (fire_frame_skip >= 5) { |
| 316 | + fire_frame_skip = 0; |
| 317 | + |
| 318 | + /* Randomize bottom row each frame to create fire source */ |
| 319 | + for (int x = 0; x < fire_w; x++) { |
| 320 | + fire_buffer[(fire_h - 1) * fire_w + x] = rand() % 256; |
| 321 | + } |
| 322 | + |
| 323 | + /* Fire propagation - lodev.org algorithm */ |
| 324 | + for (int y = 0; y < fire_h - 1; y++) { |
| 325 | + for (int x = 0; x < fire_w; x++) { |
| 326 | + /* Calculate indices with wrapping */ |
| 327 | + int y1 = (y + 1) % fire_h; |
| 328 | + int y2 = (y + 2) % fire_h; |
| 329 | + int x_left = (x - 1 + fire_w) % fire_w; |
| 330 | + int x_right = (x + 1) % fire_w; |
| 331 | + |
| 332 | + /* Average 4 neighboring pixels with exact formula */ |
| 333 | + int sum = fire_buffer[y1 * fire_w + x_left]; /* Below-left */ |
| 334 | + sum += fire_buffer[y1 * fire_w + x]; /* Below */ |
| 335 | + sum += fire_buffer[y1 * fire_w + x_right]; /* Below-right */ |
| 336 | + sum += fire_buffer[y2 * fire_w + x]; /* Two rows below */ |
| 337 | + |
| 338 | + /* Apply decay: (sum * 32) / 129 */ |
| 339 | + fire_buffer[y * fire_w + x] = (sum * 32) / 129; |
| 340 | + } |
| 341 | + } |
| 342 | + } |
| 343 | + |
| 344 | + /* Render fire masked by logo - only visible through the letters */ |
| 345 | + float palette_shift = ctx->global_time * 80.0f; |
| 346 | + |
| 347 | + for (int y = 0; y < fire_h; y++) { |
| 348 | + for (int x = 0; x < fire_w; x++) { |
| 349 | + /* Sample logo to see if we should show fire here (logo acts as mask) */ |
| 350 | + int sample_x = (x * orig_w) / logo_w; |
| 351 | + int sample_y = (y * orig_h) / logo_h; |
| 352 | + if (sample_x >= orig_w) sample_x = orig_w - 1; |
| 353 | + if (sample_y >= orig_h) sample_y = orig_h - 1; |
| 354 | + |
| 355 | + Uint8 *pixel = (Uint8*)ctx->infix_surface->pixels + sample_y * ctx->infix_surface->pitch + |
| 356 | + sample_x * ctx->infix_surface->format->BytesPerPixel; |
| 357 | + Uint32 color; |
| 358 | + memcpy(&color, pixel, ctx->infix_surface->format->BytesPerPixel); |
| 359 | + Uint8 r, g, b, a; |
| 360 | + SDL_GetRGBA(color, ctx->infix_surface->format, &r, &g, &b, &a); |
| 361 | + |
| 362 | + /* Only render fire where logo has pixels (logo is the window) */ |
| 363 | + if (a > 128) { |
| 364 | + int heat = fire_buffer[y * fire_w + x]; |
| 365 | + if (heat > 10) { /* Skip very dim pixels */ |
| 366 | + /* Simple fire palette: dark red -> bright red -> orange -> yellow */ |
| 367 | + int shifted_heat = ((int)(heat + palette_shift)) % 200; /* Cycle through lower range */ |
| 368 | + |
| 369 | + int fire_r, fire_g, fire_b; |
| 370 | + if (shifted_heat < 100) { |
| 371 | + /* Dark red to bright red */ |
| 372 | + fire_r = 128 + (shifted_heat * 127) / 100; |
| 373 | + fire_g = 0; |
| 374 | + fire_b = 0; |
| 375 | + } else { |
| 376 | + /* Bright red to yellow */ |
| 377 | + fire_r = 255; |
| 378 | + fire_g = ((shifted_heat - 100) * 255) / 100; |
| 379 | + fire_b = 0; |
| 380 | + } |
| 381 | + |
| 382 | + SDL_Rect pixel_rect = {logo_x + x, logo_y + y, 2, 2}; |
| 383 | + |
| 384 | + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_ADD); |
| 385 | + SDL_SetRenderDrawColor(ctx->renderer, fire_r, fire_g, fire_b, heat); |
| 386 | + SDL_RenderFillRect(ctx->renderer, &pixel_rect); |
| 387 | + } |
| 388 | + } |
| 389 | + } |
| 390 | + } |
| 391 | + |
| 392 | + /* Optionally draw logo outline on top for definition (with low alpha) */ |
| 393 | + SDL_Rect logo_rect = {logo_x, logo_y, logo_w, logo_h}; |
| 394 | + SDL_SetTextureAlphaMod(ctx->infix_texture, 100); |
| 395 | + SDL_RenderCopy(ctx->renderer, ctx->infix_texture, NULL, &logo_rect); |
| 396 | + SDL_SetTextureAlphaMod(ctx->infix_texture, 255); |
| 397 | + } |
| 398 | + |
| 399 | + /* Burning Wires logo in upper right corner */ |
| 400 | + if (ctx->wires_texture && ctx->wires_surface) { |
| 401 | + /* Scale to 50% size for wires logo to fit in window */ |
| 402 | + int orig_w = ctx->wires_surface->w; |
| 403 | + int orig_h = ctx->wires_surface->h; |
| 404 | + int logo_w = (int)(orig_w * 0.50f); |
| 405 | + int logo_h = (int)(orig_h * 0.50f); |
| 406 | + int logo_x = WIDTH - logo_w - 20; /* Upper right corner */ |
| 407 | + int logo_y = 20; |
| 408 | + |
| 409 | + /* Create fire effect buffer matching logo dimensions */ |
| 410 | + static Uint32 wires_fire_buffer[512 * 256]; /* Fire buffer for wires */ |
| 411 | + static int wires_fire_frame_skip = 0; |
| 412 | + int fire_w = logo_w; |
| 413 | + int fire_h = logo_h; |
| 414 | + |
| 415 | + /* Update fire every 5th frame to slow it down */ |
| 416 | + wires_fire_frame_skip++; |
| 417 | + if (wires_fire_frame_skip >= 5) { |
| 418 | + wires_fire_frame_skip = 0; |
| 419 | + |
| 420 | + /* Randomize bottom row each frame to create fire source */ |
| 421 | + for (int x = 0; x < fire_w; x++) { |
| 422 | + wires_fire_buffer[(fire_h - 1) * fire_w + x] = rand() % 256; |
| 423 | + } |
| 424 | + |
| 425 | + /* Fire propagation - lodev.org algorithm */ |
| 426 | + for (int y = 0; y < fire_h - 1; y++) { |
| 427 | + for (int x = 0; x < fire_w; x++) { |
| 428 | + /* Calculate indices with wrapping */ |
| 429 | + int y1 = (y + 1) % fire_h; |
| 430 | + int y2 = (y + 2) % fire_h; |
| 431 | + int x_left = (x - 1 + fire_w) % fire_w; |
| 432 | + int x_right = (x + 1) % fire_w; |
| 433 | + |
| 434 | + /* Average 4 neighboring pixels with exact formula */ |
| 435 | + int sum = wires_fire_buffer[y1 * fire_w + x_left]; |
| 436 | + sum += wires_fire_buffer[y1 * fire_w + x]; |
| 437 | + sum += wires_fire_buffer[y1 * fire_w + x_right]; |
| 438 | + sum += wires_fire_buffer[y2 * fire_w + x]; |
| 439 | + |
| 440 | + /* Apply decay: (sum * 32) / 129 */ |
| 441 | + wires_fire_buffer[y * fire_w + x] = (sum * 32) / 129; |
| 442 | + } |
| 443 | + } |
| 444 | + } |
| 445 | + |
| 446 | + /* Render fire masked by wires logo */ |
| 447 | + float palette_shift = ctx->global_time * 80.0f; |
| 448 | + |
| 449 | + for (int y = 0; y < fire_h; y++) { |
| 450 | + for (int x = 0; x < fire_w; x++) { |
| 451 | + /* Sample logo - scale coordinates back to original dimensions */ |
| 452 | + int sample_x = (x * orig_w) / logo_w; |
| 453 | + int sample_y = (y * orig_h) / logo_h; |
| 454 | + if (sample_x >= orig_w) sample_x = orig_w - 1; |
| 455 | + if (sample_y >= orig_h) sample_y = orig_h - 1; |
| 456 | + |
| 457 | + Uint8 *pixel = (Uint8*)ctx->wires_surface->pixels + sample_y * ctx->wires_surface->pitch + |
| 458 | + sample_x * ctx->wires_surface->format->BytesPerPixel; |
| 459 | + Uint32 color; |
| 460 | + memcpy(&color, pixel, ctx->wires_surface->format->BytesPerPixel); |
| 461 | + Uint8 r, g, b, a; |
| 462 | + SDL_GetRGBA(color, ctx->wires_surface->format, &r, &g, &b, &a); |
| 463 | + |
| 464 | + /* Only render fire where logo has pixels */ |
| 465 | + if (a > 128) { |
| 466 | + int heat = wires_fire_buffer[y * fire_w + x]; |
| 467 | + if (heat > 10) { |
| 468 | + int shifted_heat = ((int)(heat + palette_shift)) % 200; |
| 469 | + |
| 470 | + int fire_r, fire_g, fire_b; |
| 471 | + if (shifted_heat < 100) { |
| 472 | + fire_r = 128 + (shifted_heat * 127) / 100; |
| 473 | + fire_g = 0; |
| 474 | + fire_b = 0; |
| 475 | + } else { |
| 476 | + fire_r = 255; |
| 477 | + fire_g = ((shifted_heat - 100) * 255) / 100; |
| 478 | + fire_b = 0; |
| 479 | + } |
| 480 | + |
| 481 | + SDL_Rect pixel_rect = {logo_x + x, logo_y + y, 2, 2}; |
| 482 | + |
| 483 | + SDL_SetRenderDrawBlendMode(ctx->renderer, SDL_BLENDMODE_ADD); |
| 484 | + SDL_SetRenderDrawColor(ctx->renderer, fire_r, fire_g, fire_b, heat); |
| 485 | + SDL_RenderFillRect(ctx->renderer, &pixel_rect); |
| 486 | + } |
| 487 | + } |
| 488 | + } |
| 489 | + } |
| 490 | + |
| 491 | + /* Draw logo outline on top */ |
| 492 | + SDL_Rect logo_rect = {logo_x, logo_y, logo_w, logo_h}; |
| 493 | + SDL_SetTextureAlphaMod(ctx->wires_texture, 100); |
| 494 | + SDL_RenderCopy(ctx->renderer, ctx->wires_texture, NULL, &logo_rect); |
| 495 | + SDL_SetTextureAlphaMod(ctx->wires_texture, 255); |
| 496 | + } |
| 497 | + |
291 | 498 | /* Rotating textured sphere in center with Jack image */ |
292 | 499 | if (!ctx->jack_texture) return; |
293 | 500 |
|
@@ -2447,6 +2654,42 @@ int main(int argc, char *argv[]) |
2447 | 2654 | } |
2448 | 2655 | } |
2449 | 2656 |
|
| 2657 | + /* Load embedded infix.png from memory */ |
| 2658 | + SDL_RWops *infix_rw = SDL_RWFromConstMem(infix_png, infix_png_len); |
| 2659 | + if (!infix_rw) { |
| 2660 | + fprintf(stderr, "Warning: Failed to create RWops for infix: %s\n", SDL_GetError()); |
| 2661 | + ctx.infix_texture = NULL; |
| 2662 | + ctx.infix_surface = NULL; |
| 2663 | + } else { |
| 2664 | + ctx.infix_surface = IMG_Load_RW(infix_rw, 1); /* 1 = automatically close RW */ |
| 2665 | + if (!ctx.infix_surface) { |
| 2666 | + fprintf(stderr, "Warning: Failed to load embedded infix: %s\n", IMG_GetError()); |
| 2667 | + ctx.infix_texture = NULL; |
| 2668 | + } else { |
| 2669 | + /* Create and cache the texture */ |
| 2670 | + ctx.infix_texture = SDL_CreateTextureFromSurface(ctx.renderer, ctx.infix_surface); |
| 2671 | + SDL_SetTextureBlendMode(ctx.infix_texture, SDL_BLENDMODE_BLEND); |
| 2672 | + } |
| 2673 | + } |
| 2674 | + |
| 2675 | + /* Load embedded wires.png from memory */ |
| 2676 | + SDL_RWops *wires_rw = SDL_RWFromConstMem(wires_png, wires_png_len); |
| 2677 | + if (!wires_rw) { |
| 2678 | + fprintf(stderr, "Warning: Failed to create RWops for wires: %s\n", SDL_GetError()); |
| 2679 | + ctx.wires_texture = NULL; |
| 2680 | + ctx.wires_surface = NULL; |
| 2681 | + } else { |
| 2682 | + ctx.wires_surface = IMG_Load_RW(wires_rw, 1); /* 1 = automatically close RW */ |
| 2683 | + if (!ctx.wires_surface) { |
| 2684 | + fprintf(stderr, "Warning: Failed to load embedded wires: %s\n", IMG_GetError()); |
| 2685 | + ctx.wires_texture = NULL; |
| 2686 | + } else { |
| 2687 | + /* Create and cache the texture */ |
| 2688 | + ctx.wires_texture = SDL_CreateTextureFromSurface(ctx.renderer, ctx.wires_surface); |
| 2689 | + SDL_SetTextureBlendMode(ctx.wires_texture, SDL_BLENDMODE_BLEND); |
| 2690 | + } |
| 2691 | + } |
| 2692 | + |
2450 | 2693 | /* Initialize starfield */ |
2451 | 2694 | for (int i = 0; i < NUM_STARS; i++) { |
2452 | 2695 | ctx.stars[i].x = (rand() % 2000 - 1000) / 10.0f; |
|
0 commit comments