MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
asteroids3d_types.hpp
Go to the documentation of this file.
1#ifndef ASTEROIDS3D_TYPES_HPP
2#define ASTEROIDS3D_TYPES_HPP
3
5#include "mxvk/mxvk_png.hpp"
6
7#include <SDL3/SDL.h>
8
9#include <algorithm>
10#include <array>
11#include <cmath>
12#include <cstddef>
13#include <cstdint>
14#include <limits>
15#include <memory>
16#include <random>
17#include <string>
18#include <vector>
19
20#include <glm/ext/matrix_transform.hpp>
21#include <glm/glm.hpp>
22
23namespace space {
24
25 constexpr float PI = 3.14159265358979323846f;
26 constexpr int GAME_STARS = 22000;
27 constexpr int MAX_PROJECTILES = 64;
28 constexpr int MAX_ASTEROIDS = 64;
29 constexpr int MAX_PARTICLES = 6000;
30 constexpr int MAX_GENERATIONS = 1;
31 constexpr int CHILDREN_PER_SPAWN = 2;
32 constexpr int LARGE_ASTEROID_POINTS = 20;
33 constexpr int MEDIUM_ASTEROID_POINTS = 50;
34 constexpr int SMALL_ASTEROID_POINTS = 100;
35 constexpr float PROJECTILE_SPEED = 52.0f;
36 constexpr float PROJECTILE_LIFETIME = 3.0f;
37 constexpr int FIRE_COOLDOWN = 5;
38 constexpr int SHOTS_PER_BURST = 5;
39 constexpr int FIRE_DELAY = 3;
40 constexpr int EXPLOSION_DURATION_FRAMES = 90;
41 constexpr float ROUND_TIME_LIMIT_SECONDS = 270.0f;
42 constexpr float SHIP_MODEL_SCALE = 1.55f;
43 constexpr float ASTEROID_SHIP_COLLISION_SCALE = 1.08f;
44 constexpr float ASTEROID_PROJECTILE_COLLISION_SCALE = 1.18f;
45 constexpr glm::vec4 PROJECTILE_COLOR{1.0f, 0.58f, 0.12f, 1.0f};
46 constexpr Sint16 CONTROLLER_DEAD_ZONE = 8000;
47 constexpr float CONTROLLER_AXIS_MAX = 32767.0f;
48 constexpr float BOUNDARY_X_MIN = -150.0f;
49 constexpr float BOUNDARY_X_MAX = 150.0f;
50 constexpr float BOUNDARY_Y_MIN = -100.0f;
51 constexpr float BOUNDARY_Y_MAX = 100.0f;
52 constexpr float BOUNDARY_Z_MIN = -150.0f;
53 constexpr float BOUNDARY_Z_MAX = 150.0f;
54 constexpr float BOUNDARY_BOUNCE_FACTOR = 1.2f;
55
56 enum class GameMode { Intro, Loading, Playing, GameComplete, GameOver };
57
58 inline std::default_random_engine &rng() {
59 static thread_local std::default_random_engine engine{std::random_device{}()};
60 return engine;
61 }
62
63 inline float random_float(float min_value, float max_value) {
64 std::uniform_real_distribution<float> dist(min_value, max_value);
65 return dist(rng());
66 }
67
68 inline int random_int(int min_value, int max_value) {
69 std::uniform_int_distribution<int> dist(min_value, max_value);
70 return dist(rng());
71 }
72
73 inline SDL_Surface *load_color_keyed_png(const std::string &path, std::uint8_t threshold = 12, std::uint8_t softness = 48) {
74 SDL_Surface *loaded_surface = mxvk::LoadPNG(path.c_str());
75 if (loaded_surface == nullptr) {
76 throw mxvk::Exception("Failed to load PNG: " + path);
77 }
78
79 SDL_Surface *surface = SDL_ConvertSurface(loaded_surface, SDL_PIXELFORMAT_RGBA32);
80 SDL_DestroySurface(loaded_surface);
81 if (surface == nullptr) {
82 throw mxvk::Exception("Failed to convert PNG to RGBA: " + path);
83 }
84
85 const SDL_PixelFormatDetails *format_details = SDL_GetPixelFormatDetails(surface->format);
86 if (format_details == nullptr) {
87 SDL_DestroySurface(surface);
88 throw mxvk::Exception("Failed to query pixel format details for: " + path);
89 }
90
91 if (!SDL_LockSurface(surface)) {
92 SDL_DestroySurface(surface);
93 throw mxvk::Exception("Failed to lock PNG surface: " + path);
94 }
95
96 auto *pixels = static_cast<std::uint32_t *>(surface->pixels);
97 const int pixel_count = surface->w * surface->h;
98
99 struct KeyedPixel {
100 std::uint8_t r = 0;
101 std::uint8_t g = 0;
102 std::uint8_t b = 0;
103 std::uint8_t a = 0;
104 };
105
106 std::vector<KeyedPixel> keyed(static_cast<std::size_t>(pixel_count));
107 for (int i = 0; i < pixel_count; ++i) {
108 std::uint8_t r = 0;
109 std::uint8_t g = 0;
110 std::uint8_t b = 0;
111 std::uint8_t a = 0;
112 SDL_GetRGBA(pixels[i], format_details, nullptr, &r, &g, &b, &a);
113 const int brightness = std::max({static_cast<int>(r), static_cast<int>(g), static_cast<int>(b)});
114 if (brightness <= threshold) {
115 keyed[static_cast<std::size_t>(i)] = {r, g, b, 0};
116 continue;
117 }
118 const int soft_end = static_cast<int>(threshold) + static_cast<int>(softness);
119 if (brightness < soft_end) {
120 const float t = static_cast<float>(brightness - threshold) / static_cast<float>(std::max<int>(1, softness));
121 a = static_cast<std::uint8_t>(std::clamp(static_cast<int>(std::lround(static_cast<float>(a) * t)), 0, 255));
122 }
123 keyed[static_cast<std::size_t>(i)] = {r, g, b, a};
124 }
125
126 constexpr int COLOR_BLEED_PASSES = 5;
127 for (int pass = 0; pass < COLOR_BLEED_PASSES; ++pass) {
128 std::vector<KeyedPixel> next = keyed;
129 for (int y = 0; y < surface->h; ++y) {
130 for (int x = 0; x < surface->w; ++x) {
131 const int index = y * surface->w + x;
132 if (keyed[static_cast<std::size_t>(index)].a != 0) {
133 continue;
134 }
135
136 int red = 0;
137 int green = 0;
138 int blue = 0;
139 int count = 0;
140 for (int oy = -1; oy <= 1; ++oy) {
141 for (int ox = -1; ox <= 1; ++ox) {
142 if (ox == 0 && oy == 0) {
143 continue;
144 }
145 const int nx = x + ox;
146 const int ny = y + oy;
147 if (nx < 0 || ny < 0 || nx >= surface->w || ny >= surface->h) {
148 continue;
149 }
150 const KeyedPixel &neighbor = keyed[static_cast<std::size_t>(ny * surface->w + nx)];
151 if (neighbor.a == 0) {
152 continue;
153 }
154 red += neighbor.r;
155 green += neighbor.g;
156 blue += neighbor.b;
157 ++count;
158 }
159 }
160 if (count > 0) {
161 KeyedPixel &out = next[static_cast<std::size_t>(index)];
162 out.r = static_cast<std::uint8_t>(red / count);
163 out.g = static_cast<std::uint8_t>(green / count);
164 out.b = static_cast<std::uint8_t>(blue / count);
165 }
166 }
167 }
168 keyed = std::move(next);
169 }
170
171 for (int i = 0; i < pixel_count; ++i) {
172 const KeyedPixel &px = keyed[static_cast<std::size_t>(i)];
173 pixels[i] = SDL_MapRGBA(format_details, nullptr, px.r, px.g, px.b, px.a);
174 }
175
176 SDL_UnlockSurface(surface);
177 return surface;
178 }
179
180 inline glm::vec3 normalize_or_zero(const glm::vec3 &value) {
181 const float len = glm::length(value);
182 if (len <= 1e-6f) {
183 return glm::vec3(0.0f, 0.0f, -1.0f);
184 }
185 return value / len;
186 }
187
188 inline glm::mat4 build_model_matrix(const glm::vec3 &position, const glm::vec3 &rotation_degrees, float scale, const glm::vec3 &center_offset) {
189 glm::mat4 model(1.0f);
190 model = glm::translate(model, position);
191 model = glm::rotate(model, glm::radians(rotation_degrees.y), glm::vec3(0.0f, 1.0f, 0.0f));
192 model = glm::rotate(model, glm::radians(rotation_degrees.x), glm::vec3(1.0f, 0.0f, 0.0f));
193 model = glm::rotate(model, glm::radians(rotation_degrees.z), glm::vec3(0.0f, 0.0f, 1.0f));
194 model = glm::scale(model, glm::vec3(scale));
195 model = glm::translate(model, center_offset);
196 return model;
197 }
198
199 struct Projectile {
200 glm::vec3 position{0.0f};
201 glm::vec3 prev_position{0.0f};
202 glm::vec3 velocity{0.0f};
203 glm::vec4 color{1.0f, 0.58f, 0.12f, 1.0f};
204 float lifetime = 0.0f;
205 bool active = false;
206 };
207
208 struct Asteroid {
209 glm::vec3 position{0.0f};
210 glm::vec3 velocity{0.0f};
211 glm::vec3 rotation{0.0f};
212 glm::vec3 rotation_speed{0.0f};
213 float radius = 0.0f;
214 bool active = false;
215 int generation = 0;
216 int model_index = 0;
217 };
218
219 struct ShipCollisionSample {
220 glm::vec3 local_position{0.0f};
221 float radius = 0.0f;
222 };
223
224 struct Particle {
225 glm::vec3 position{0.0f};
226 glm::vec3 velocity{0.0f};
227 glm::vec4 color{1.0f};
228 float size = 0.0f;
229 float lifetime = 0.0f;
230 float max_lifetime = 0.0f;
231 bool color_flash = false;
232 bool active = false;
233 };
234
235 struct FlameVertex {
236 glm::vec3 pos{};
237 glm::vec4 color{};
238 };
239
240 struct FlamePushConstants {
241 glm::mat4 mvp{1.0f};
242 glm::vec4 params{0.0f};
243 };
244
245 struct Star {
246 glm::vec3 position{0.0f};
247 glm::vec3 velocity{0.0f};
248 glm::vec4 base_color{1.0f};
249 glm::vec4 color{1.0f};
250 float size = 1.0f;
251 float brightness = 1.0f;
252 float twinkle_phase = 0.0f;
253 float twinkle_speed = 1.0f;
254 int layer = 0;
255 };
256
257} // namespace space
258
259#endif
PNG image loading and saving utilities via SDL3.
SDL_Surface * LoadPNG(const char *file)
Load a PNG file into an SDL_Surface.
Definition mxvk_png.cpp:89
GameMode
High-level state of the Asteroids application.
@ Loading
Asset-loading screen.
@ GameComplete
Completed game.
@ GameOver
Player has no remaining lives.
@ Playing
Active gameplay.
glm::vec3 normalize_or_zero(const glm::vec3 &value)
Normalizes a direction with a stable fallback.
constexpr float ROUND_TIME_LIMIT_SECONDS
Multiplayer round limit in seconds.
glm::mat4 build_model_matrix(const glm::vec3 &position, const glm::vec3 &rotation_degrees, float scale, const glm::vec3 &center_offset)
Builds a translated, rotated, scaled model matrix.
int random_int(int min_value, int max_value)
Generates a uniformly distributed integer.
std::default_random_engine & rng()
Returns the thread-local random number engine used by simulation helpers.
constexpr int SMALL_ASTEROID_POINTS
Score awarded for a small asteroid.
constexpr int FIRE_COOLDOWN
Frames between firing bursts.
constexpr float BOUNDARY_X_MAX
Maximum simulation x-coordinate.
constexpr int MAX_GENERATIONS
Maximum asteroid split generation.
constexpr int CHILDREN_PER_SPAWN
Child asteroids created by a split.
SDL_Surface * load_color_keyed_png(const std::string &path, std::uint8_t threshold=12, std::uint8_t softness=48)
Loads a PNG and fades dark color-key pixels to transparency.
constexpr float ASTEROID_PROJECTILE_COLLISION_SCALE
Projectile collision-radius adjustment.
constexpr int MAX_PROJECTILES
Maximum locally simulated projectiles.
constexpr float BOUNDARY_Y_MAX
Maximum simulation y-coordinate.
constexpr int MAX_ASTEROIDS
Maximum locally simulated asteroids.
constexpr int MAX_PARTICLES
Maximum particles in the shared particle pool.
constexpr float BOUNDARY_Y_MIN
Minimum simulation y-coordinate.
constexpr float ASTEROID_SHIP_COLLISION_SCALE
Ship collision-radius adjustment.
constexpr glm::vec4 PROJECTILE_COLOR
Default projectile color.
constexpr int SHOTS_PER_BURST
Projectiles fired in one burst.
constexpr int EXPLOSION_DURATION_FRAMES
Ship explosion duration in frames.
constexpr int FIRE_DELAY
Frames between shots within a burst.
constexpr float BOUNDARY_X_MIN
Minimum simulation x-coordinate.
constexpr int LARGE_ASTEROID_POINTS
Score awarded for a large asteroid.
constexpr float BOUNDARY_BOUNCE_FACTOR
Boundary collision response multiplier.
constexpr float PROJECTILE_SPEED
Projectile travel speed in world units per second.
constexpr float BOUNDARY_Z_MIN
Minimum simulation z-coordinate.
constexpr int MEDIUM_ASTEROID_POINTS
Score awarded for a medium asteroid.
constexpr float PI
Single-precision value of pi.
constexpr float SHIP_MODEL_SCALE
Uniform ship model scale.
constexpr float PROJECTILE_LIFETIME
Projectile lifetime in seconds.
constexpr Sint16 CONTROLLER_DEAD_ZONE
Controller axis dead-zone magnitude.
constexpr float CONTROLLER_AXIS_MAX
Maximum signed controller axis magnitude.
float random_float(float min_value, float max_value)
Generates a uniformly distributed floating-point value.
constexpr int GAME_STARS
Number of stars in the background field.
constexpr float BOUNDARY_Z_MAX
Maximum simulation z-coordinate.
Runtime state for an asteroid.
bool active
Whether this slot is active.
glm::vec3 rotation_speed
Angular velocity in degrees per second.
int model_index
Asteroid model variant.
glm::vec3 rotation
Euler rotation in degrees.
glm::vec3 velocity
World-space velocity.
float radius
Collision radius.
int generation
Split generation used to determine size and scoring.
glm::vec3 position
Current world-space position.
Push-constant payload for the engine-flame shaders.
glm::vec4 params
Shader-specific animation parameters.
glm::mat4 mvp
Model-view-projection matrix.
Vertex consumed by the procedural engine-flame pipeline.
glm::vec4 color
Vertex color.
glm::vec3 pos
Vertex position.
Runtime state for an explosion or engine particle.
float max_lifetime
Initial lifetime in seconds.
bool color_flash
Whether the particle flashes as it ages.
bool active
Whether this slot is active.
glm::vec3 position
Current world-space position.
float lifetime
Remaining lifetime in seconds.
float size
Rendered point size.
glm::vec3 velocity
World-space velocity.
glm::vec4 color
Render color.
Runtime state for a ship projectile.
bool active
Whether this slot is active.
glm::vec3 position
Current world-space position.
glm::vec3 velocity
World-space velocity.
float lifetime
Remaining lifetime in seconds.
glm::vec3 prev_position
Position from the previous simulation step.
glm::vec4 color
Render color.
One spherical sample used by the compound ship collision shape.
float radius
Sample sphere radius.
glm::vec3 local_position
Sample center in ship-local space.
Runtime state for one animated background star.
glm::vec3 position
World-space position.
glm::vec4 base_color
Color before brightness modulation.
float brightness
Base brightness multiplier.
float twinkle_phase
Twinkle animation phase.
glm::vec3 velocity
World-space velocity.
float size
Rendered point size.
int layer
Parallax layer index.
glm::vec4 color
Current rendered color.
float twinkle_speed
Twinkle animation rate.