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