Skip to content

Instantly share code, notes, and snippets.

@jweinst1
Created July 25, 2026 00:30
Show Gist options
  • Select an option

  • Save jweinst1/f2dad5764e3c97c51393b6a23cd01171 to your computer and use it in GitHub Desktop.

Select an option

Save jweinst1/f2dad5764e3c97c51393b6a23cd01171 to your computer and use it in GitHub Desktop.
glass shattering game in C++
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include <cmath>
#include <vector>
#include <random>
// Linearly interpolate between two points
inline SDL_FPoint lerp(const SDL_FPoint& a, const SDL_FPoint& b, float t) {
return SDL_FPoint{
a.x + t * (b.x - a.x),
a.y + t * (b.y - a.y)
};
}
// Returns a random point on segment AB, constrained between minPercent and maxPercent (0.0 to 1.0)
SDL_FPoint getRandomPointOnSegment(const SDL_FPoint& a, const SDL_FPoint& b,
float minPercent = 0.20f, float maxPercent = 0.80f)
{
// Thread-local random number generator initialized once per thread
static thread_local std::mt19937 gen(std::random_device{}());
// Distribution for t in the specified range [minPercent, maxPercent]
std::uniform_real_distribution<float> dist(minPercent, maxPercent);
float t = dist(gen);
return lerp(a, b, t);
}
// Calculates the X and Y screen coordinates for a circle centered anywhere
void get_arbitrary_circle_point_trig(
float centerX, float centerY,
float radius, float angle_radians,
float* out_x, float* out_y
) {
// 1. Calculate the local x distance, then shift it by the center X
*out_x = centerX + (radius * cosf(angle_radians));
// 2. Calculate the local y distance, then shift it by the center Y
*out_y = centerY + (radius * sinf(angle_radians));
}
void gen_circle_points(float centerX,
float centerY,
float radius,
SDL_FPoint* points) {
int total_steps = (int)(2.0f * SDL_PI_F * radius);
float angle_step = (2.0f * SDL_PI_F) / total_steps;
for (int i = 0; i < total_steps; i++)
{
float angle = i * angle_step;
float x, y;
get_arbitrary_circle_point_trig(centerX, centerY, radius, angle, &x, &y);
points->x = x;
points->y = y;
++points;
}
}
// treat x y as if it was x z for a transform, semi 3d looking circle overlay for sphere
void transform_z_points(const SDL_FPoint* src, SDL_FPoint* dst, int amount, float centerY) {
for (int i = 0; i < amount; ++i) {
dst[i].x = src[i].x;
// 1. Get distance from center (Local Y)
float localY = src[i].y - centerY;
// 2. Squash/tilt it around its center, then move back to screen position
dst[i].y = centerY + (localY * 0.5f); // low tilt is more constrained
}
}
// Returns the 2D cross product of vector AB and AP
inline float edgeFunction(const SDL_FPoint& a, const SDL_FPoint& b, const SDL_FPoint& p) {
return (b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x);
}
bool isPointInTriangle(const SDL_FPoint& p, const SDL_FPoint& a, const SDL_FPoint& b, const SDL_FPoint& c) {
float w0 = edgeFunction(a, b, p);
float w1 = edgeFunction(b, c, p);
float w2 = edgeFunction(c, a, p);
// Check if all three results have the same sign
bool has_neg = (w0 < 0.0f) || (w1 < 0.0f) || (w2 < 0.0f);
bool has_pos = (w0 > 0.0f) || (w1 > 0.0f) || (w2 > 0.0f);
// If it contains both positive and negative results, the point lies outside
return !(has_neg && has_pos);
}
inline SDL_FPoint getMidwayPoint(const SDL_FPoint& a , const SDL_FPoint& b) {
return SDL_FPoint{(a.x + b.x) / 2.0f, (a.y + b.y) / 2.0f};
}
// todo fracture
// todo different procjectile fracture differently
struct Triangle {
SDL_Vertex verts[3];
Triangle() = default;
Triangle(const SDL_Vertex& v1,
const SDL_Vertex& v2,
const SDL_Vertex& v3) {
verts[0] = v1;
verts[1] = v2;
verts[2] = v3;
}
Triangle(const SDL_FPoint& p1,
const SDL_FPoint& p2,
const SDL_FPoint& p3,
const SDL_FColor& c1, const SDL_FColor& c2, const SDL_FColor& c3) {
verts[0].position = p1;
verts[0].color = c1;
verts[1].position = p2;
verts[1].color = c2;
verts[2].position = p3;
verts[2].color = c3;
}
void twoWayFracture01(Triangle* out1, Triangle* out2) {
SDL_FPoint midPoint = getMidwayPoint(verts[0].position, verts[1].position);
SDL_FColor newColor{static_cast<float>(verts[0].color.r + 0.09),
static_cast<float>(verts[0].color.g + 0.09),
static_cast<float>(verts[0].color.b + 0.09),
static_cast<float>(verts[0].color.a)};
// now make two triangles from 2 and mid
out1->verts[0] = SDL_Vertex{midPoint, verts[0].color};
out1->verts[1] = SDL_Vertex{verts[2].position, verts[2].color};
out1->verts[2] = SDL_Vertex{verts[1].position, verts[1].color};
out2->verts[0] = SDL_Vertex{midPoint, newColor};
out2->verts[1] = SDL_Vertex{verts[2].position, newColor};
out2->verts[2] = SDL_Vertex{verts[0].position, newColor};
}
bool containsPoint(const SDL_FPoint& p) {
return isPointInTriangle(p, verts[0].position, verts[1].position, verts[2].position);
}
void setVert(size_t i, float x, float y, float r, float g, float b, float a) {
SDL_Vertex* vert = &verts[i];
vert->position.x = x;
vert->position.y = y;
vert->color.r = r;
vert->color.g = g;
vert->color.b = b;
vert->color.a = a;
}
};
struct GlassWindow {
float posX = 0.0f;
float posY = 0.0f;
float sizeX = 0.0f;
float sizeY = 0.0f;
SDL_FColor baseColor{0.0f, 0.3f, 1.0f, 0.7f};
std::vector<Triangle> pieces;
void init(float x, float y, float xsize, float ysize) {
posX = x;
posY = y;
sizeX = xsize;
sizeY = ysize;
// first two triangles
SDL_Vertex v1{{posX, posY}, baseColor};
SDL_Vertex v2{{posX + sizeX, posY}, baseColor};
SDL_Vertex v3{{posX, posY + sizeY}, baseColor};
SDL_Vertex v4{{posX + sizeX, posY + sizeY}, baseColor};
SDL_Vertex v5{{posX + sizeX, posY}, baseColor};
SDL_Vertex v6{{posX, posY + sizeY}, baseColor};
Triangle t1{v1, v2, v3};
Triangle t2{v4, v5, v6};
pieces.push_back(t1);
pieces.push_back(t2);
}
void draw(SDL_Renderer* rend) {
for (int i = 0; i < pieces.size(); ++i)
{
SDL_RenderGeometry(rend, NULL, pieces[i].verts, 3, NULL, 0);
}
}
void checkAndDoFracture(const SDL_FPoint& p) {
std::vector<Triangle> newTriangles;
for (int i = 0; i < pieces.size(); ++i) {
if (pieces[i].containsPoint(p)) {
Triangle n1;
Triangle n2;
pieces[i].twoWayFracture01(&n1, &n2);
newTriangles.push_back(n1);
newTriangles.push_back(n2);
} else {
newTriangles.push_back(pieces[i]);
}
}
pieces = newTriangles; // todo optimize
}
};
int main(int argc, char *argv[])
{
bool quit = false;
SDL_Window *window = SDL_CreateWindow("Triangle Example", 800, 600, 0);
SDL_Renderer *renderer = SDL_CreateRenderer(window, NULL);
GlassWindow gw;
gw.init(300.0f, 300.0f, 100.0f, 100.0f);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
while (!quit) {
SDL_Event event;
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_EVENT_QUIT) {
quit = true;
}
// Detect Mouse Clicks in SDL3
else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
if (event.button.button == SDL_BUTTON_LEFT) {
// SDL3 provides mouse coordinates as clean floats
float mousepre_x = event.button.x;
float mousepre_y = event.button.y;
std::cout << "Clicked x=" << mousepre_x << ", y=" << mousepre_y
<< "\n";
SDL_FPoint clickedPoint{mousepre_x, mousepre_y};
gw.checkAndDoFracture(clickedPoint);
}
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
//SDL_SetRenderDrawColor(renderer, 233, 0, 0, 255);
//SDL_RenderPoints(renderer, topPoints, circlePointCount);
//SDL_SetRenderDrawColor(renderer, 0, 233, 0, 255);
//SDL_RenderPoints(renderer, topPoints2, circlePointCount);
gw.draw(renderer);
SDL_Delay(16);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment