载入中...
搜索中...
未找到
ParticlePlayback.cpp
浏览该文件的文档.
2
3#include "common/Module.h"
4#include "graphics/Graphics.h"
5
6#include <algorithm>
7#include <cmath>
8#include <random>
9
10namespace eve::particles {
11
13 const float playbackScale = cfg.resolvedParameterScale("playback");
14 if (sim.paused || dt <= 0.f || cfg.playbackSpeed <= 0.f || playbackScale <= 0.f) return 0.f;
15
16 float scaledDt = dt * cfg.playbackSpeed * playbackScale;
17 if (cfg.maxDeltaTime > 0.f && scaledDt > cfg.maxDeltaTime) scaledDt = cfg.maxDeltaTime;
18
19 if (cfg.fixedTimeStep <= 0.f) {
20 stepEmitterSim(cfg, sim, scaledDt);
21 return scaledDt;
22 }
23
24 sim.fixedTimeAccum += scaledDt;
25 const float step = cfg.maxDeltaTime > 0.f ? std::min(cfg.fixedTimeStep, cfg.maxDeltaTime) : cfg.fixedTimeStep;
26 const int maxSteps = cfg.maxSubSteps > 0 ? cfg.maxSubSteps : 1;
27 int steps = 0;
28 while (sim.fixedTimeAccum + 1e-7f >= step && steps < maxSteps) {
29 stepEmitterSim(cfg, sim, step);
30 sim.fixedTimeAccum -= step;
31 ++steps;
32 }
33
34 // Bound retained debt as well as work per frame. A suspended process must
35 // not spend many later frames catching up and producing a VFX burst.
36 if (steps == maxSteps && sim.fixedTimeAccum >= step) sim.fixedTimeAccum = std::fmod(sim.fixedTimeAccum, step);
37 return float(steps) * step;
38}
39
42 if (step.delta.nanoseconds() < 0)
44 "particle simulation delta must be non-negative"));
45 if (sim.hasSimulationTick && step.tick <= sim.simulationTick)
47 eve::DiagnosticCode::Conflict, "particle simulation tick must advance monotonically"));
48
49 const float dt = static_cast<float>(step.delta.seconds());
50 if (!std::isfinite(dt))
52 "particle simulation delta is outside float range"));
53
54 // Scheduler-owned pause, rate and fixed-step policy are already reflected
55 // in the supplied SimulationStep. The legacy emitter playback controls are
56 // intentionally not consulted here.
57 if (!sim.paused && dt > 0.f) stepEmitterSim(cfg, sim, dt);
58 sim.simulationTick = step.tick;
59 sim.hasSimulationTick = true;
62}
63
67
69 config()->emissionRateOverDistance = rate > 0.f ? rate : 0.f;
70}
71
72float ParticleEmitter::getEmissionRateOverDistance() { return config()->emissionRateOverDistance; }
73
74void ParticleEmitter::setLooping(bool looping) { config()->looping = looping; }
75bool ParticleEmitter::getLooping() { return config()->looping; }
76
77void ParticleEmitter::setPlaybackSpeed(float speed) { config()->playbackSpeed = speed > 0.f ? speed : 0.f; }
78
79float ParticleEmitter::getPlaybackSpeed() { return config()->playbackSpeed; }
80
81void ParticleEmitter::setFixedTimeStep(float seconds, int maxSubSteps) {
82 auto c = config();
83 c->fixedTimeStep = seconds > 0.f ? seconds : 0.f;
84 c->maxSubSteps = maxSubSteps > 0 ? maxSubSteps : 1;
85 sim()->fixedTimeAccum = 0.f;
86}
87
88float ParticleEmitter::getFixedTimeStep() { return config()->fixedTimeStep; }
89
91 auto c = config();
92 auto s = sim();
93 c->randomSeed = seed;
94 c->autoRandomSeed = false;
95 s->activeSeed = seed;
96 s->rng.seed(static_cast<std::mt19937::result_type>(seed));
97}
98
99int ParticleEmitter::getRandomSeed() { return config()->randomSeed; }
100
101void ParticleEmitter::setAutoRandomSeed(bool enabled) { config()->autoRandomSeed = enabled; }
102bool ParticleEmitter::getAutoRandomSeed() { return config()->autoRandomSeed; }
103
105 auto c = config();
106 auto s = sim();
107 int seed = c->randomSeed;
108 if (c->autoRandomSeed) {
109 std::random_device rd;
110 seed = static_cast<int>(rd());
111 }
112 s->activeSeed = seed;
113 s->rng.seed(static_cast<std::mt19937::result_type>(seed));
114 s->active = true;
115 s->paused = false;
116 s->emitterAge = 0.f;
117 s->fixedTimeAccum = 0.f;
118 s->distanceEmitAccum = 0.f;
119 for (auto& b : c->bursts) b.emitted = false;
120 const float prewarm = c->prewarmSeconds;
121 if (prewarm > 0.f) {
122 constexpr float kPrewarmDt = 1.f / 60.f;
123 const int steps = int(std::ceil(prewarm / kPrewarmDt));
124 for (int i = 0; i < steps; ++i) stepEmitterSim(*c, *s, kPrewarmDt);
125 }
126}
127
129 auto s = sim();
130 s->active = false;
131 s->paused = false;
132 s->emitAccum = 0.f;
133 s->distanceEmitAccum = 0.f;
134 s->fixedTimeAccum = 0.f;
135 s->emitterAge = 0.f;
136 s->lastX = config()->x;
137 s->lastY = config()->y;
138 s->hasLastPos = true;
139}
140
142 auto s = sim();
143 if (s->active) s->paused = true;
144}
145
147 auto s = sim();
148 s->alive = 0;
149 s->emitAccum = 0.f;
150 s->distanceEmitAccum = 0.f;
151 s->fixedTimeAccum = 0.f;
152 s->emitterAge = 0.f;
153 s->hasLastPos = false;
154 s->overflowWarned = false;
155 for (auto& b : config()->bursts) b.emitted = false;
156 for (auto& p : s->particles) p.life = 0.f;
157 auto g = gpuSim();
158 if (g->residentHandle != 0) {
159 auto* gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
160 if (gfx) gfx->resetGpuParticleEmitter(g->residentHandle);
161 }
162 g->residentActive = false;
163 g->estimatedAlive = 0;
164 g->timeline = 0.0;
165 g->deathTimes.clear();
166}
167
169 if (count <= 0) return;
170 auto c = config();
171 auto s = sim();
172 // Spawn at the CURRENT attached position so manual bursts do not lag a
173 // frame behind animation or IK updates.
174 syncAttach();
175 for (int i = 0; i < count; ++i) spawnParticle(*c, *s);
176}
177
179 auto s = sim();
180 return s->active && !s->paused;
181}
182bool ParticleEmitter::isPaused() { return sim()->paused; }
183bool ParticleEmitter::isStopped() { return !sim()->active; }
184
186 auto g = gpuSim();
187 return g->residentActive ? g->estimatedAlive + sim()->alive : sim()->alive;
188}
189int ParticleEmitter::getBufferSize() { return int(sim()->particles.size()); }
190
191} // namespace eve::particles
const std::string & s
glm::vec4 p[6]
tensor::Graph g
Definition GpuGraph.cpp:7
std::int32_t c
MeleePoint3 b
Definition MeleeHit.cpp:41
std::uint32_t seed
Definition PointSet.cpp:807
int steps
std::uint32_t count
float step
Definition TreeMesh.cpp:314
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
void setRandomSeed(int seed)
Select a deterministic seed and disable automatic seeding.
void setPlaybackSpeed(float speed)
Scale simulation time; zero freezes playback.
void syncAttach()
Sync Config.x/y (and direction) from the attached bone. Also called by ParticleSimSystem.
bool getLooping()
Return whether the finite emitter timeline repeats.
bool getAutoRandomSeed()
Return whether start() generates a new random seed.
void setAutoRandomSeed(bool enabled)
Generate a new random seed whenever start() is called.
float getEmissionRateOverDistance()
Return particles emitted per world unit travelled.
eve::Result< void > advance(const eve::SimulationStep &step)
Advance this emitter by one scheduler-owned simulation step.
void setEmissionRateOverDistance(float rate)
Set particles emitted per world unit travelled; zero disables it.
void setFixedTimeStep(float seconds, int maxSubSteps=8)
Configure fixed stepping and the per-frame catch-up limit; zero seconds disables it.
float getPlaybackSpeed()
Return the simulation time multiplier.
void setLooping(bool looping)
Enable or disable repeating a finite emitter timeline.
float getFixedTimeStep()
Return the fixed step in seconds, or zero for variable stepping.
int getRandomSeed()
Return the configured deterministic seed.
EVENGINE_API_DOMAINS float advanceEmitterSim(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim, float dt)
Apply playback speed and optional bounded fixed stepping before simulation.
bool spawnParticle(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim)
Spawn particle.
void stepEmitterSim(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim, float dt)
Step emitter sim.
bool enabled
One deterministic fixed-step emitted by SimulationClock.
Definition Time.h:158
float maxDeltaTime
Cap per-step delta time (0 = unlimited).
float resolvedParameterScale(const std::string &target) const
Resolved parameter scale.
float playbackSpeed
Simulation time multiplier. 0 freezes playback without changing pause state.
int maxSubSteps
Spiral-of-death guard for fixed stepping.
float fixedTimeStep
Fixed simulation step in seconds (0 = variable step).
eve::SimulationTick simulationTick
Last scheduler tick observed by the checked simulation API.