Created
July 25, 2026 00:30
-
-
Save jweinst1/f2dad5764e3c97c51393b6a23cd01171 to your computer and use it in GitHub Desktop.
glass shattering game in C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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