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
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
63
64 inline std::default_random_engine &rng() {
65 static thread_local std::default_random_engine engine{std::random_device{}()};
66 return engine;
67 }
68
69 inline float random_float(float min_value, float max_value) {
70 std::uniform_real_distribution<float> dist(min_value, max_value);
71 return dist(rng());
72 }
73
74 inline int random_int(int min_value, int max_value) {
75 std::uniform_int_distribution<int> dist(min_value, max_value);
76 return dist(rng());
77 }
78
79 inline SDL_Surface *load_color_keyed_png(const std::string &path, std::uint8_t threshold = 12, std::uint8_t softness = 48) {
80 SDL_Surface *loaded_surface = mxvk::LoadPNG(path.c_str());
81 if (loaded_surface == nullptr) {
82 throw mxvk::Exception("Failed to load PNG: " + path);
83 }
84
85 SDL_Surface *surface = SDL_ConvertSurface(loaded_surface, SDL_PIXELFORMAT_RGBA32);
86 SDL_DestroySurface(loaded_surface);
87 if (surface == nullptr) {
88 throw mxvk::Exception("Failed to convert PNG to RGBA: " + path);
89 }
90
91 const SDL_PixelFormatDetails *format_details = SDL_GetPixelFormatDetails(surface->format);
92 if (format_details == nullptr) {
93 SDL_DestroySurface(surface);
94 throw mxvk::Exception("Failed to query pixel format details for: " + path);
95 }
96
97 if (!SDL_LockSurface(surface)) {
98 SDL_DestroySurface(surface);
99 throw mxvk::Exception("Failed to lock PNG surface: " + path);
100 }
101
102 auto *pixels = static_cast<std::uint32_t *>(surface->pixels);
103 const int pixel_count = surface->w * surface->h;
104
105 struct KeyedPixel {
106 std::uint8_t r = 0;
107 std::uint8_t g = 0;
108 std::uint8_t b = 0;
109 std::uint8_t a = 0;
110 };
111
112 std::vector<KeyedPixel> keyed(static_cast<std::size_t>(pixel_count));
113 for (int i = 0; i < pixel_count; ++i) {
114 std::uint8_t r = 0;
115 std::uint8_t g = 0;
116 std::uint8_t b = 0;
117 std::uint8_t a = 0;
118 SDL_GetRGBA(pixels[i], format_details, nullptr, &r, &g, &b, &a);
119 const int brightness = std::max({static_cast<int>(r), static_cast<int>(g), static_cast<int>(b)});
120 if (brightness <= threshold) {
121 keyed[static_cast<std::size_t>(i)] = {r, g, b, 0};
122 continue;
123 }
124 const int soft_end = static_cast<int>(threshold) + static_cast<int>(softness);
125 if (brightness < soft_end) {
126 const float t = static_cast<float>(brightness - threshold) / static_cast<float>(std::max<int>(1, softness));
127 a = static_cast<std::uint8_t>(std::clamp(static_cast<int>(std::lround(static_cast<float>(a) * t)), 0, 255));
128 }
129 keyed[static_cast<std::size_t>(i)] = {r, g, b, a};
130 }
131
132 constexpr int COLOR_BLEED_PASSES = 5;
133 for (int pass = 0; pass < COLOR_BLEED_PASSES; ++pass) {
134 std::vector<KeyedPixel> next = keyed;
135 for (int y = 0; y < surface->h; ++y) {
136 for (int x = 0; x < surface->w; ++x) {
137 const int index = y * surface->w + x;
138 if (keyed[static_cast<std::size_t>(index)].a != 0) {
139 continue;
140 }
141
142 int red = 0;
143 int green = 0;
144 int blue = 0;
145 int count = 0;
146 for (int oy = -1; oy <= 1; ++oy) {
147 for (int ox = -1; ox <= 1; ++ox) {
148 if (ox == 0 && oy == 0) {
149 continue;
150 }
151 const int nx = x + ox;
152 const int ny = y + oy;
153 if (nx < 0 || ny < 0 || nx >= surface->w || ny >= surface->h) {
154 continue;
155 }
156 const KeyedPixel &neighbor = keyed[static_cast<std::size_t>(ny * surface->w + nx)];
157 if (neighbor.a == 0) {
158 continue;
159 }
160 red += neighbor.r;
161 green += neighbor.g;
162 blue += neighbor.b;
163 ++count;
164 }
165 }
166 if (count > 0) {
167 KeyedPixel &out = next[static_cast<std::size_t>(index)];
168 out.r = static_cast<std::uint8_t>(red / count);
169 out.g = static_cast<std::uint8_t>(green / count);
170 out.b = static_cast<std::uint8_t>(blue / count);
171 }
172 }
173 }
174 keyed = std::move(next);
175 }
176
177 for (int i = 0; i < pixel_count; ++i) {
178 const KeyedPixel &px = keyed[static_cast<std::size_t>(i)];
179 pixels[i] = SDL_MapRGBA(format_details, nullptr, px.r, px.g, px.b, px.a);
180 }
181
182 SDL_UnlockSurface(surface);
183 return surface;
184 }
185
186 inline glm::vec3 normalize_or_zero(const glm::vec3 &value) {
187 const float len = glm::length(value);
188 if (len <= 1e-6f) {
189 return glm::vec3(0.0f, 0.0f, -1.0f);
190 }
191 return value / len;
192 }
193
194 inline glm::mat4 build_model_matrix(const glm::vec3 &position,
195 const glm::vec3 &rotation_degrees,
196 float scale,
197 const glm::vec3 &center_offset) {
198 glm::mat4 model(1.0f);
199 model = glm::translate(model, position);
200 model = glm::rotate(model, glm::radians(rotation_degrees.y), glm::vec3(0.0f, 1.0f, 0.0f));
201 model = glm::rotate(model, glm::radians(rotation_degrees.x), glm::vec3(1.0f, 0.0f, 0.0f));
202 model = glm::rotate(model, glm::radians(rotation_degrees.z), glm::vec3(0.0f, 0.0f, 1.0f));
203 model = glm::scale(model, glm::vec3(scale));
204 model = glm::translate(model, center_offset);
205 return model;
206 }
207
208 struct Projectile {
209 glm::vec3 position{0.0f};
210 glm::vec3 prev_position{0.0f};
211 glm::vec3 velocity{0.0f};
212 glm::vec4 color{1.0f, 0.58f, 0.12f, 1.0f};
213 float lifetime = 0.0f;
214 bool active = false;
215 };
216
217 struct Asteroid {
218 glm::vec3 position{0.0f};
219 glm::vec3 velocity{0.0f};
220 glm::vec3 rotation{0.0f};
221 glm::vec3 rotation_speed{0.0f};
222 float radius = 0.0f;
223 bool active = false;
224 int generation = 0;
225 int model_index = 0;
226 };
227
228 struct ShipCollisionSample {
229 glm::vec3 local_position{0.0f};
230 float radius = 0.0f;
231 };
232
233 struct Particle {
234 glm::vec3 position{0.0f};
235 glm::vec3 velocity{0.0f};
236 glm::vec4 color{1.0f};
237 float size = 0.0f;
238 float lifetime = 0.0f;
239 float max_lifetime = 0.0f;
240 bool color_flash = false;
241 bool active = false;
242 };
243
244 struct FlameVertex {
245 glm::vec3 pos{};
246 glm::vec4 color{};
247 };
248
249 struct FlamePushConstants {
250 glm::mat4 mvp{1.0f};
251 glm::vec4 params{0.0f};
252 };
253
254 struct Star {
255 glm::vec3 position{0.0f};
256 glm::vec3 velocity{0.0f};
257 glm::vec4 base_color{1.0f};
258 glm::vec4 color{1.0f};
259 float size = 1.0f;
260 float brightness = 1.0f;
261 float twinkle_phase = 0.0f;
262 float twinkle_speed = 1.0f;
263 int layer = 0;
264 };
265
266} // namespace space
267
268#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.
@ Loading
Asset-loading screen.
@ Intro
Introductory 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.