From 9d8910627537ae797cd114afde41dce4055c827c Mon Sep 17 00:00:00 2001 From: Gregor Klevze Date: Sun, 30 Nov 2025 16:48:45 +0100 Subject: [PATCH] fix particle --- src/main.cpp | 135 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 39 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 65747c6..b540136 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -523,27 +523,37 @@ static bool helpOverlayPausedGame = false; static void drawBlockTexture(SDL_Renderer* renderer, SDL_Texture* blocksTex, float x, float y, float size, int blockType); // ----------------------------------------------------------------------------- struct BlockParticle { - float x, y, vx, vy, size, alpha, decay; - int blockType; // 0..6 + float x{}, y{}; + float vx{}, vy{}; + float size{}, alpha{}, decay{}; + float wobblePhase{}, wobbleSpeed{}; + float coreHeat{}; BlockParticle(float sx, float sy) : x(sx), y(sy) { - float angle = (rand() % 628) / 100.0f; // 0..2pi - float speed = 1.5f + (rand() % 350) / 100.0f; // ~1.5..5.0 - vx = std::cos(angle) * speed; - vy = std::sin(angle) * speed; - size = 6.0f + (rand() % 50) / 10.0f; // 6..11 px + const float spreadDeg = 35.0f; + const float angleDeg = -90.0f + spreadDeg * ((rand() % 200) / 100.0f - 1.0f); // bias upward + const float angleRad = angleDeg * 3.1415926f / 180.0f; + float speed = 1.3f + (rand() % 220) / 80.0f; // ~1.3..4.05 + vx = std::cos(angleRad) * speed * 0.55f; + vy = std::sin(angleRad) * speed; + size = 6.0f + (rand() % 40) / 10.0f; // 6..10 px alpha = 1.0f; - decay = 0.012f + (rand() % 200) / 10000.0f; // 0.012..0.032 - blockType = rand() % 7; // choose a tetris color + decay = 0.0095f + (rand() % 180) / 12000.0f; // 0.0095..0.0245 + wobblePhase = (rand() % 628) / 100.0f; + wobbleSpeed = 0.08f + (rand() % 60) / 600.0f; + coreHeat = 0.65f + (rand() % 35) / 100.0f; } bool update() { - vx *= 0.985f; // friction - vy = vy * 0.985f + 0.07f; // gravity + vx *= 0.992f; + vy = vy * 0.985f - 0.015f; // buoyancy pushes upward (negative vy) x += vx; y += vy; + wobblePhase += wobbleSpeed; + x += std::sin(wobblePhase) * 0.12f; alpha -= decay; - size = std::max(2.0f, size - 0.04f); - return alpha > 0.02f; + size = std::max(1.8f, size - 0.03f); + coreHeat = std::max(0.0f, coreHeat - decay * 0.6f); + return alpha > 0.03f; } }; @@ -592,36 +602,83 @@ static void updateFireworks(double frameMs) { } // Primary implementation that accepts a texture pointer -static void drawFireworks_impl(SDL_Renderer* renderer, SDL_Texture* blocksTexture) { +static SDL_Color blendFireColor(float heat, float alphaScale, Uint8 minG, Uint8 minB) { + heat = std::clamp(heat, 0.0f, 1.0f); + Uint8 r = 255; + Uint8 g = static_cast(std::clamp(120.0f + heat * (255.0f - 120.0f), float(minG), 255.0f)); + Uint8 b = static_cast(std::clamp(40.0f + (1.0f - heat) * 60.0f, float(minB), 255.0f)); + Uint8 a = static_cast(std::clamp(alphaScale * 255.0f, 0.0f, 255.0f)); + return SDL_Color{r, g, b, a}; +} + +static void drawFireworks_impl(SDL_Renderer* renderer, SDL_Texture*) { + SDL_BlendMode previousBlend = SDL_BLENDMODE_NONE; + SDL_GetRenderDrawBlendMode(renderer, &previousBlend); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_ADD); + + static constexpr int quadIndices[6] = {0, 1, 2, 2, 1, 3}; + + auto makeVertex = [](float px, float py, SDL_Color c) { + SDL_Vertex v{}; + v.position.x = px; + v.position.y = py; + v.color = SDL_FColor{ + c.r / 255.0f, + c.g / 255.0f, + c.b / 255.0f, + c.a / 255.0f + }; + return v; + }; + for (auto& f : fireworks) { - // Particle draw uses the texture pointer passed into drawBlockTexture calls from f.draw - // We'll set a thread-local-ish variable by passing the texture as an argument to draw - // routines or using the provided texture in the particle's draw path. - // For simplicity, the particle draw function below will reference a global symbol - // via an argument — we adapt by providing the texture when calling drawBlockTexture. - // Implementation: call a small lambda that temporarily binds the texture for drawBlockTexture. - struct Drawer { SDL_Renderer* r; SDL_Texture* tex; void drawParticle(struct BlockParticle& p) { - if (tex) { - Uint8 prevA = 255; - SDL_GetTextureAlphaMod(tex, &prevA); - Uint8 setA = Uint8(std::max(0.0f, std::min(1.0f, p.alpha)) * 255.0f); - SDL_SetTextureAlphaMod(tex, setA); - // Note: color modulation will be applied by callers of drawBlockTexture where needed - // but we mimic behavior from previous implementation by leaving color mod as default. - drawBlockTexture(r, tex, p.x - p.size * 0.5f, p.y - p.size * 0.5f, p.size, p.blockType); - SDL_SetTextureAlphaMod(tex, prevA); - SDL_SetTextureColorMod(tex, 255, 255, 255); - } else { - SDL_SetRenderDrawColor(r, 255, 255, 255, Uint8(p.alpha * 255)); - SDL_FRect rect{p.x - p.size/2, p.y - p.size/2, p.size, p.size}; - SDL_RenderFillRect(r, &rect); - } - } - } drawer{renderer, blocksTexture}; for (auto &p : f.particles) { - drawer.drawParticle(p); + const float heat = std::clamp(p.alpha * 1.25f + p.coreHeat * 0.5f, 0.0f, 1.0f); + SDL_Color glowColor = blendFireColor(0.45f + heat * 0.55f, p.alpha * 0.55f, 100, 40); + SDL_Color tailBaseColor = blendFireColor(heat * 0.75f, p.alpha * 0.5f, 70, 25); + SDL_Color tailTipColor = blendFireColor(heat * 0.35f, p.alpha * 0.2f, 40, 15); + SDL_Color coreColor = blendFireColor(heat, std::min(1.0f, p.alpha * 1.1f), 150, 80); + + float velLen = std::sqrt(p.vx * p.vx + p.vy * p.vy); + SDL_FPoint dir = velLen > 0.001f ? SDL_FPoint{p.vx / velLen, p.vy / velLen} + : SDL_FPoint{0.0f, -1.0f}; + SDL_FPoint perp{-dir.y, dir.x}; + + const float baseWidth = std::max(0.8f, p.size * 0.55f); + const float tipWidth = baseWidth * 0.35f; + const float tailLen = p.size * (3.0f + (1.0f - p.alpha) * 1.8f); + + SDL_FPoint base{p.x, p.y}; + SDL_FPoint tip{p.x + dir.x * tailLen, p.y + dir.y * tailLen}; + + SDL_Vertex tailVerts[4]; + tailVerts[0] = makeVertex(base.x + perp.x * baseWidth, base.y + perp.y * baseWidth, tailBaseColor); + tailVerts[1] = makeVertex(base.x - perp.x * baseWidth, base.y - perp.y * baseWidth, tailBaseColor); + tailVerts[2] = makeVertex(tip.x + perp.x * tipWidth, tip.y + perp.y * tipWidth, tailTipColor); + tailVerts[3] = makeVertex(tip.x - perp.x * tipWidth, tip.y - perp.y * tipWidth, tailTipColor); + SDL_RenderGeometry(renderer, nullptr, tailVerts, 4, quadIndices, 6); + + const float glowAlong = p.size * 0.95f; + const float glowAcross = p.size * 0.6f; + SDL_Vertex glowVerts[4]; + glowVerts[0] = makeVertex(base.x + dir.x * glowAlong, base.y + dir.y * glowAlong, glowColor); + glowVerts[1] = makeVertex(base.x - dir.x * glowAlong, base.y - dir.y * glowAlong, glowColor); + glowVerts[2] = makeVertex(base.x + perp.x * glowAcross, base.y + perp.y * glowAcross, glowColor); + glowVerts[3] = makeVertex(base.x - perp.x * glowAcross, base.y - perp.y * glowAcross, glowColor); + SDL_RenderGeometry(renderer, nullptr, glowVerts, 4, quadIndices, 6); + + const float coreWidth = p.size * 0.35f; + const float coreHeight = p.size * 0.9f; + SDL_Vertex coreVerts[4]; + coreVerts[0] = makeVertex(base.x + perp.x * coreWidth, base.y + perp.y * coreWidth, coreColor); + coreVerts[1] = makeVertex(base.x - perp.x * coreWidth, base.y - perp.y * coreWidth, coreColor); + coreVerts[2] = makeVertex(base.x + dir.x * coreHeight, base.y + dir.y * coreHeight, coreColor); + coreVerts[3] = makeVertex(base.x - dir.x * coreHeight, base.y - dir.y * coreHeight, coreColor); + SDL_RenderGeometry(renderer, nullptr, coreVerts, 4, quadIndices, 6); } } + + SDL_SetRenderDrawBlendMode(renderer, previousBlend); } // External wrappers for use by other translation units (MenuState) // Expect callers to pass the blocks texture via StateContext so we avoid globals.