- BillboardInstance: per-instance uv sub-rect, feet anchor, turn, dithered alpha, flash; BillboardBatch::sharedTexture; Texture createEmpty/update/cellUV/cellAnchor/nearest - SpriteAtlas: fixed-cell runtime atlas with gutters - hd2d::ui: Octopath-style panels, fonts (Cinzel, EB Garamond), dialogue box with typewriter + portraits + choices, menus, nameplates, toasts, ImGui theme - DayCycle (dawn/day/dusk/night keyframes, sun + moon arcs), Weather overcast/mist/ lightning, night-aware presets, stars and moon in the sky - Hidden-window offscreen captures (Window visible=false, Renderer output FBO, OffscreenTarget); --screenshot never opens a window - Camera::worldToScreen; CMake usable via add_subdirectory (HD2D_BUILD_DEMO, HD2D_ENGINE_ASSET_DIR)
87 lines
3.1 KiB
C++
87 lines
3.1 KiB
C++
#pragma once
|
|
#include "hd2d/GL.h"
|
|
|
|
#include <glm/glm.hpp>
|
|
#include <random>
|
|
#include <vector>
|
|
|
|
namespace hd2d {
|
|
|
|
struct PointLight;
|
|
|
|
// High-resolution (non pixel-art) particles: ambient fields (fireflies, dust),
|
|
// fire emitters (flames, embers, smoke, glow + flickering light) and bursts.
|
|
class ParticleSystem {
|
|
public:
|
|
// Must match the kind ids in particles.vert / particles.frag.
|
|
enum class Kind : int {
|
|
Firefly = 0, // blinking emissive glow, wanders near the ground
|
|
Mote = 1, // dust speck only visible where sunlight hits it (god rays)
|
|
Spark = 2, // ballistic burst particle (footstep dust, rain splashes, ...)
|
|
Flame = 3, // additive tongue of fire, eroded by noise as it ages
|
|
Ember = 4, // tiny hot spark drifting up on turbulent air
|
|
Smoke = 5, // alpha-blended puff rising above a fire
|
|
Glow = 6, // large soft halo around a fire (one per emitter per frame)
|
|
};
|
|
|
|
struct Field { // region that keeps a constant population of ambient particles
|
|
Kind kind;
|
|
glm::vec3 min, max;
|
|
int count;
|
|
};
|
|
|
|
struct Fire {
|
|
glm::vec3 pos; // base of the flames
|
|
float scale = 1.0f; // overall size / intensity
|
|
float flameAccum = 0.0f, emberAccum = 0.0f, smokeAccum = 0.0f;
|
|
float flicker = 1.0f; // current light flicker multiplier
|
|
float phase = 0.0f;
|
|
};
|
|
|
|
~ParticleSystem();
|
|
void addField(Kind kind, glm::vec3 min, glm::vec3 max, int count);
|
|
int addFire(glm::vec3 pos, float scale = 1.0f);
|
|
void burst(glm::vec3 pos, int count, glm::vec3 color, float speed, float size, float life);
|
|
void update(float dt, float time);
|
|
void draw() const;
|
|
// Appends fire lights first, then up to `maxFireflies` firefly lights.
|
|
void collectLights(std::vector<PointLight>& out, int maxFireflies) const;
|
|
const std::vector<Fire>& fires() const { return fires_; }
|
|
|
|
glm::vec3 fireflyColor{2.6f, 3.2f, 0.9f};
|
|
glm::vec3 moteColor{1.0f, 0.95f, 0.8f};
|
|
float fireflyLightRadius = 2.2f;
|
|
glm::vec3 fireLightColor{4.0f, 1.75f, 0.55f};
|
|
float fireLightRadius = 7.0f;
|
|
glm::vec3 wind{0.0f}; // world-space drift applied to smoke and embers
|
|
float ambientFade = 1.0f; // scales fireflies + motes (Weather lowers it in rain / mist)
|
|
float fireflyGlow = 1.0f; // firefly brightness + light (WeatherLook::fireflyGlow, raised at night)
|
|
|
|
private:
|
|
struct Particle {
|
|
glm::vec3 pos, vel;
|
|
glm::vec3 color;
|
|
float size, age, life, phase;
|
|
Kind kind;
|
|
int field; // ambient field index, or -1
|
|
int fire; // owning fire, or -1
|
|
};
|
|
struct GpuParticle {
|
|
glm::vec4 posSize;
|
|
glm::vec4 color; // rgb, a = alpha
|
|
glm::vec4 params; // x = kind, y = age / life, z = random seed
|
|
};
|
|
void spawnInField(Particle& p, int fieldIndex, bool randomAge);
|
|
void spawnFire(const Fire& f, int fireIndex, Kind kind);
|
|
|
|
std::vector<Field> fields_;
|
|
std::vector<Fire> fires_;
|
|
std::vector<Particle> particles_;
|
|
std::vector<GpuParticle> gpu_;
|
|
std::mt19937 rng_{1337};
|
|
GLuint vao_ = 0, vbo_ = 0;
|
|
size_t capacity_ = 0;
|
|
};
|
|
|
|
} // namespace hd2d
|