// Sphere atlas dumping function
bool dumpSphereAtlas(int bodyNum, sBody* pBody)
{
    if (!pBody) return false;

    int numPrims = (int)pBody->m_primitives.size();
    if (numPrims == 0) return false;

    // Count sphere primitives
    int numSpheres = 0;
    for (int i = 0; i < numPrims; i++)
    {
        if (isPrimSphere(&pBody->m_primitives[i]))
            numSpheres++;
    }

    if (numSpheres == 0)
        return false; // No spheres to dump

    // Create a simple atlas: each sphere gets a small circular billboard texture
    // We'll arrange them in a grid, each cell is 32x32 pixels
    const int cellSize = 32;
    const int cellsPerRow = 8;
    int numRows = (numSpheres + cellsPerRow - 1) / cellsPerRow;
    int atlasW = cellsPerRow * cellSize;
    int atlasH = numRows * cellSize;

    // Round up to power of 2
    auto nextPow2 = [](int v) -> int {
        v--;
        v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16;
        return v + 1;
    };
    atlasW = nextPow2(std::max(atlasW, 64));
    atlasH = nextPow2(std::max(atlasH, 64));

    // Create the atlas image (RGBA)
    std::vector<unsigned char> image(atlasW * atlasH * 4, 0);

    // Render each sphere as a circular gradient billboard
    int sphereIdx = 0;
    for (int i = 0; i < numPrims; i++)
    {
        sPrimitive* pPrim = &pBody->m_primitives[i];
        if (!isPrimSphere(pPrim)) continue;

        // Get color from palette
        unsigned char r = 128, g = 128, b = 128;
        int palIdx = pPrim->m_color;
        r = (unsigned char)RGB_Pal[palIdx * 4 + 0];
        g = (unsigned char)RGB_Pal[palIdx * 4 + 1];
        b = (unsigned char)RGB_Pal[palIdx * 4 + 2];

        // Calculate cell position
        int cellX = (sphereIdx % cellsPerRow) * cellSize;
        int cellY = (sphereIdx / cellsPerRow) * cellSize;

        // Draw a circular billboard with radial gradient
        float centerX = cellX + cellSize * 0.5f;
        float centerY = cellY + cellSize * 0.5f;
        float radius = cellSize * 0.4f; // Leave some padding

        for (int y = 0; y < cellSize; y++)
        {
            for (int x = 0; x < cellSize; x++)
            {
                int pixelX = cellX + x;
                int pixelY = cellY + y;
                if (pixelX >= atlasW || pixelY >= atlasH) continue;

                float dx = (pixelX + 0.5f) - centerX;
                float dy = (pixelY + 0.5f) - centerY;
                float dist = sqrtf(dx * dx + dy * dy);

                if (dist <= radius)
                {
                    // Smooth circular falloff for billboard effect
                    float t = dist / radius;
                    float alpha = 1.0f - (t * t); // Quadratic falloff
                    alpha = std::max(0.0f, std::min(1.0f, alpha));

                    int offset = (pixelY * atlasW + pixelX) * 4;
                    image[offset + 0] = r;
                    image[offset + 1] = g;
                    image[offset + 2] = b;
                    image[offset + 3] = (unsigned char)(alpha * 255);
                }
            }
        }

        sphereIdx++;
    }

    // Write the PNG
    ensureAtlasDir();
    char buf[64];
    snprintf(buf, sizeof(buf), "spheres_%03d.png", bodyNum);
    std::string path = getAtlasFolder() + buf;
    int result = stbi_write_png(path.c_str(), atlasW, atlasH, 4, image.data(), atlasW * 4);

    if (result)
    {
        printf(RNDR_INFO "Sphere atlas dumped: %s (%dx%d, %d spheres)" CON_RESET "\n", path.c_str(), atlasW, atlasH, numSpheres);
    }
    else
    {
        printf(RNDR_ERR "Failed to write sphere atlas: %s" CON_RESET "\n", path.c_str());
    }

    return result != 0;
}
