载入中...
搜索中...
未找到
ParticleEmitter.h
浏览该文件的文档.
1#pragma once
2
3#include "common/ECS.h"
5#include "graphics/Color.h"
7
8#include <cstdint>
9#include <memory>
10#include <random>
11#include <string>
12#include <vector>
13
14namespace eve::graphics {
15class Graphics;
16class Camera2D;
17class Canvas;
18class Light2D;
19class Shader;
20class Texture;
21}
22
23namespace eve::animation {
24class AnimPose;
25class AnimSkeleton;
26class AnimSkin;
27class SpineSkeleton;
28}
29
30namespace eve::ik {
31class Skeleton2D;
32class Skeleton3D;
33}
34
35namespace eve::gpgpu {
36class GpuBuffer;
37}
38
39namespace eve::particles {
40
41// Color lives in eve::graphics (see graphics/Canvas.h); re-expose it here so
42// particle code keeps the unqualified form.
44
46struct Particle {
47 float x = 0.f;
48 float y = 0.f;
49 float vx = 0.f;
50 float vy = 0.f;
51 float ax = 0.f;
52 float ay = 0.f;
53 float radial = 0.f;
54 float tangential = 0.f;
55 float life = 0.f;
56 float lifetime = 1.f;
57 float size = 1.f;
58 float rot = 0.f;
59 float spin = 0.f;
61 float frame = 0.f;
63 float noisePhase = 0.f;
64};
65
70class ParticleEmitter : public ecs::Entity {
71public:
72 ENTITY(ParticleEmitter, ecs::Entity)
73
74 void release() override {}
75
76 struct Config {
78 struct Burst {
79 float time = 0.f;
80 int count = 0;
81 bool emitted = false;
82 };
83
84 float x = 0.f;
85 float y = 0.f;
86 float emissionRate = 0.f;
87 float lifeMin = 1.f;
88 float lifeMax = 1.f;
89 float emitterLife = -1.f; // -1 = forever
90 float direction = 0.f;
91 float spread = 0.f;
92 float speedMin = 0.f;
93 float speedMax = 0.f;
94 float accelXMin = 0.f, accelYMin = 0.f;
95 float accelXMax = 0.f, accelYMax = 0.f;
96 float radialMin = 0.f, radialMax = 0.f;
97 float tangentialMin = 0.f, tangentialMax = 0.f;
98 float particleW = 8.f;
99 float particleH = 8.f;
100 float sizeStart = 1.f;
101 float sizeEnd = 1.f;
102 float sizeVariation = 0.f; // 0..1
103 float spinMin = 0.f;
104 float spinMax = 0.f;
106 float gravityX = 0.f;
107 float gravityY = 0.f;
109 float damping = 0.f;
111 float limitVelocity = 0.f;
115 float inheritVelocity = 0.f;
117 std::string simSpace = "world";
119 float noiseStrength = 0.f;
120 float noiseFrequency = 1.f;
121 float noiseSpeed = 1.f;
123 std::string collisionMode = "none";
124 float collisionRadius = 0.f; // 0 = particle size/2
126 float collisionLifetimeLoss = 0.f; // fraction of life removed per hit
128 float boundsMinX = 0.f;
129 float boundsMinY = 0.f;
130 float boundsMaxX = 0.f;
131 float boundsMaxY = 0.f;
133 bool worldCollision = false;
135 std::string renderMode = "billboard";
136 float stretchFactor = 1.f;
138 std::string overflowMode = "drop";
140 float maxDeltaTime = 0.f;
142 float prewarmSeconds = 0.f;
144 bool gpuSimulation = false;
145 std::vector<Burst> bursts;
147 struct ForceField {
148 float x = 0.f;
149 float y = 0.f;
150 float radius = 0.f;
151 float strength = 0.f;
152 float falloff = 1.f; // exponent; 1 = linear
153 };
154 std::vector<ForceField> forceFields;
156 struct LightCfg {
157 bool enabled = false;
158 int max = 4; // capped at 8 per emitter (engine limit per canvas)
159 float radius = 120.f;
160 float intensity = 1.f;
161 float r = 1.f;
162 float g = 1.f;
163 float b = 1.f;
166 struct SubEmitter {
168 std::string trigger = "birth"; // "birth" | "death" | "collision"
169 float inheritVelocity = 0.f;
170 };
171 std::vector<SubEmitter> subEmitters;
173 float startRotMin = 0.f;
174 float startRotMax = 0.f;
176 int hframes = 1;
177 int vframes = 1;
178 float frameRate = 0.f;
180 float frameRandomStart = 0.f;
188 std::string areaType = "none";
189 float areaX = 0.f;
190 float areaY = 0.f;
191 Color colorStart{1.f, 1.f, 1.f, 1.f};
192 Color colorEnd{1.f, 1.f, 1.f, 0.f};
194 };
195
196 struct Sim {
197 std::vector<Particle> particles;
198 int alive = 0;
199 float emitAccum = 0.f;
200 float emitterAge = 0.f;
201 bool active = false;
202 bool paused = false;
203 bool hasLastPos = false;
204 float lastX = 0.f;
205 float lastY = 0.f;
206 bool overflowWarned = false;
207 std::mt19937 rng;
208 };
209
210 struct Draw {
212 graphics::Canvas *canvas = nullptr; // nullptr → screen
213 graphics::Camera2D *camera = nullptr; // nullptr → screen space (no camera)
215 graphics::Shader *shader = nullptr; // custom fragment pipeline (textured quads only)
216 int layer = 0;
217 bool visible = true;
218 };
219
221 struct Resource {
222 std::string path;
223 std::string texturePath;
224 int64_t modtime = -1;
225 bool autoReload = true;
226 };
227
238 struct Attach {
239 enum class Kind { None, AnimPose, Spine, Ik2D, Ik3D };
240
243 animation::AnimSkeleton *skeleton = nullptr; // optional (name lookup)
247 int boneIndex = -1;
248 float offsetX = 0.f;
249 float offsetY = 0.f;
250 float offsetZ = 0.f;
252 std::string plane = "xy";
253 float scale = 1.f;
254 bool followRotation = false;
255 bool enabled = false;
256 };
257
262 struct SkinSource {
265 animation::AnimSkeleton *skeleton = nullptr; // optional (name filter)
266 int filterBone = -1; // skeleton bone index, -1 = all
267 float minWeight = 0.f;
269 std::string plane = "xy";
270 float scale = 1.f;
271 bool enabled = false;
273 std::vector<int> candidates;
274 bool candidatesDirty = true;
276 };
277
279 struct Lights {
280 std::vector<graphics::Light2D *> pool;
281 };
282
284 struct GpuSim {
285 bool enabled = false;
286 bool initialized = false;
287 bool failed = false;
288 std::shared_ptr<eve::gpgpu::GpuBuffer> buffer;
289 std::vector<float> mirror; // CPU staging for pack/upload and readback
290 };
291
292 COMPONENT(Config, config)
293 COMPONENT(Sim, sim)
294 COMPONENT(Draw, draw)
295 COMPONENT(Resource, resource)
296 COMPONENT(Attach, attach)
297 COMPONENT(SkinSource, skinSource)
298 COMPONENT(Lights, lights)
299 COMPONENT(GpuSim, gpuSim)
300
301 static ParticleEmitter *createEmitter(int bufferSize = 1000);
302
303 void setPosition(float x, float y);
304 void moveTo(float x, float y);
305 float getX();
306 float getY();
307
308 void setEmissionRate(float rate);
309 float getEmissionRate();
310
311 void setParticleLifetime(float minLife, float maxLife);
314
315 void setEmitterLifetime(float seconds);
316 float getEmitterLifetime();
317
318 void setDirection(float radians);
319 float getDirection();
320
321 void setSpread(float radians);
322 float getSpread();
323
324 void setSpeed(float minSpeed, float maxSpeed);
325 void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax);
326 void setRadialAcceleration(float minA, float maxA);
327 void setTangentialAcceleration(float minA, float maxA);
328
329 void setEmissionArea(const std::string &type, float x, float y);
330 std::string getEmissionAreaType();
331 float getEmissionAreaX();
332 float getEmissionAreaY();
333
334 void setParticleSize(float width, float height);
335 float getParticleWidth();
336 float getParticleHeight();
337
338 void setSizes(float startScale, float endScale);
339 void setSizeVariation(float variation);
340 float getSizeVariation();
341
342 void setSpin(float minSpin, float maxSpin);
343 void setStartRotation(float minDeg, float maxDeg);
344
345 void addBurst(float time, int count);
346 void clearBursts();
347 void setPrewarm(float seconds);
348 float getPrewarmSeconds();
349
350 void setGravity(float x, float y);
351 void setDamping(float perSecond);
352 void setLimitVelocity(float maxSpeed);
353 void clearVelocityCurve();
354 void addVelocityCurvePoint(float t, float v);
355 void setInheritVelocity(float fraction);
356 void setSimulationSpace(const std::string &space);
357 void setNoise(float strength, float frequency = 1.f, float speed = 1.f);
358 void setGpuSimulation(bool enable);
359 bool getGpuSimulation();
360
361 void setCollision(const std::string &mode, float radius = 0.f, float restitution = 0.6f,
362 float lifetimeLoss = 0.f);
363 void setCollisionBounds(bool enabled, float minX, float minY, float maxX, float maxY);
364 void setWorldCollision(bool enabled);
365
366 void setRenderMode(const std::string &mode, float stretchFactor = 1.f);
367 void setOverflowMode(const std::string &mode);
368 void setMaxDeltaTime(float seconds);
369
370 void addSubEmitter(ParticleEmitter *target, const std::string &trigger,
371 float inheritVelocity = 0.f);
372 void clearSubEmitters();
373
374 void addForceField(float x, float y, float radius, float strength, float falloff = 1.f);
375 void clearForceFields();
376
379
380 void setLights(bool enabled, float radius = 120.f, float intensity = 1.f, float r = 1.f,
381 float g = 1.f, float b = 1.f, int maxLights = 4);
382 bool getLightsEnabled();
383
384 void setBlendMode(const std::string &mode);
385 std::string getBlendMode();
386
387 void setFlipbook(int hframes, int vframes, float framesPerSecond = 0.f,
388 float randomStart = 0.f);
389
390 void clearColorGradient();
391 void addColorStop(float t, float r, float g, float b, float a);
392 void clearSizeCurve();
393 void addSizeCurvePoint(float t, float v);
394 void clearRotationCurve();
395 void addRotationCurvePoint(float t, float v);
396
397 void setColorStart(float r, float g, float b, float a = 1.f);
398 void setColorEnd(float r, float g, float b, float a = 1.f);
399
400 void setTexture(graphics::Texture *texture);
402
403 void setCanvas(graphics::Canvas *canvas);
404 void setCamera(graphics::Camera2D *camera);
405
406 void setLayer(int layer);
407 int getLayer();
408
409 void setVisible(bool visible);
410 bool isVisible();
411
412 void start();
413 void stop();
414 void pause();
415 void reset();
416 void emit(int count);
417
418 bool isActive();
419 bool isPaused();
420 bool isStopped();
421
422 int getCount();
423 int getBufferSize();
424
426 void applyPreset(const std::string &name);
427
428 bool applyConfig(const std::string &json);
429 bool loadConfig(const std::string &path);
430 bool reloadConfig();
431 void setAutoReload(bool enable);
432 bool getAutoReload();
433 std::string getConfigPath();
434
435 // --- Bone attachment (3D AnimPose / 2D Spine / IK 2D·3D) ---
436 void attachToBone(animation::AnimPose *pose, int boneIndex);
438 const std::string &boneName);
439 void attachToSpineBone(animation::SpineSkeleton *spine, int boneIndex);
440 void attachToSpineBoneByName(animation::SpineSkeleton *spine, const std::string &boneName);
441 void attachToSkeleton2D(eve::ik::Skeleton2D *skeleton, int boneId);
442 void attachToSkeleton3D(eve::ik::Skeleton3D *skeleton, int boneId);
443 void setAttachOffset(float x, float y, float z);
444 void setAttachPlane(const std::string &plane);
445 void setAttachScale(float scale);
446 void setFollowBoneRotation(bool enable);
447 void detach();
448 bool isAttached();
449 int getAttachBone();
451 std::string getAttachKind();
453 void syncAttach();
454
455 // --- Skinned mesh surface emission ---
457 void setSkinBoneFilter(int skeletonBoneIndex, float minWeight = 0.f);
458 void setSkinBoneFilterByName(animation::AnimSkeleton *skeleton, const std::string &boneName,
459 float minWeight = 0.f);
460 void setSkinPlane(const std::string &plane);
461 void setSkinScale(float scale);
462 void clearSkinSource();
463 bool hasSkinSource();
465 void emitFromSkin(int count);
466};
467
469void spawnParticleAt(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim, float x, float y);
473 ParticleEmitter::GpuSim &gpu, float dt);
475using WorldCollisionFn = bool (*)(float x, float y, float radius, float &nx, float &ny);
482 float &outY);
483
484} // namespace eve::particles
std::string type
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
int x
Definition Grass.cpp:135
uint32_t a
uint32_t b
int width
TileLayer * layer
Shader * shader
const char * name
Definition RockMesh.cpp:21
SettlementPipeline::Stage fn
bool enabled
int v
float scale
Definition TreeMesh.cpp:122
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:15
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
CPU linear-blend skinning binding for one mesh against an AnimSkeleton.
Definition AnimSkin.h:25
Runtime Spine skeleton pose (local + world bone transforms, slot attachments). Script type: SpineSkel...
Declarative 2D camera (viewport center + zoom).
Custom GPU program.
Definition Shader.h:30
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:17
Script-facing 2D skeleton + pose state (ik::skeleton2d + ik::ecs2d). Bone indices are stable after ea...
Definition Skeleton2D.h:12
Script-facing 3D skeleton + pose state (ik::skeleton3d + ik::ecs3d). Local angles are yaw/pitch in th...
Definition Skeleton3D.h:11
Multi-point scalar curve sampled with linear interpolation between points. Empty curve means "use the...
ECS emitter entity. Script configures components; ParticleSimSystem / ParticleRenderSystem drive per-...
void applyPreset(const std::string &name)
Named preset: "spark" / "smoke" / "fire". Unknown → no-op.
void addSizeCurvePoint(float t, float v)
void setFlipbook(int hframes, int vframes, float framesPerSecond=0.f, float randomStart=0.f)
void addColorStop(float t, float r, float g, float b, float a)
void setSkinBoneFilter(int skeletonBoneIndex, float minWeight=0.f)
void addVelocityCurvePoint(float t, float v)
void attachToSkeleton2D(eve::ik::Skeleton2D *skeleton, int boneId)
void attachToSkeleton3D(eve::ik::Skeleton3D *skeleton, int boneId)
void setSpin(float minSpin, float maxSpin)
void setTexture(graphics::Texture *texture)
void setPosition(float x, float y)
void setRenderMode(const std::string &mode, float stretchFactor=1.f)
void attachToSpineBone(animation::SpineSkeleton *spine, int boneIndex)
static ParticleEmitter * createEmitter(int bufferSize=1000)
void setSkinBoneFilterByName(animation::AnimSkeleton *skeleton, const std::string &boneName, float minWeight=0.f)
bool loadConfig(const std::string &path)
void setCollisionBounds(bool enabled, float minX, float minY, float maxX, float maxY)
std::string getAttachKind()
"none" | "anim" | "spine" | "ik2d" | "ik3d"
void setSkinPlane(const std::string &plane)
void setSizes(float startScale, float endScale)
void setRadialAcceleration(float minA, float maxA)
void setSpeed(float minSpeed, float maxSpeed)
void setBlendMode(const std::string &mode)
void addForceField(float x, float y, float radius, float strength, float falloff=1.f)
void setCanvas(graphics::Canvas *canvas)
void attachToBone(animation::AnimPose *pose, int boneIndex)
void setOverflowMode(const std::string &mode)
void setStartRotation(float minDeg, float maxDeg)
void setCamera(graphics::Camera2D *camera)
void setLinearAcceleration(float xmin, float ymin, float xmax, float ymax)
void emitFromSkin(int count)
Burst-emit count particles from the current skinned surface.
void syncAttach()
Sync Config.x/y (and direction) from the attached bone. Also called by ParticleSimSystem.
void setTangentialAcceleration(float minA, float maxA)
bool applyConfig(const std::string &json)
void setSkinSource(animation::AnimSkin *skin, animation::AnimPose *pose)
void addRotationCurvePoint(float t, float v)
void setCollision(const std::string &mode, float radius=0.f, float restitution=0.6f, float lifetimeLoss=0.f)
void addSubEmitter(ParticleEmitter *target, const std::string &trigger, float inheritVelocity=0.f)
void setColorEnd(float r, float g, float b, float a=1.f)
void setEmissionArea(const std::string &type, float x, float y)
void setColorStart(float r, float g, float b, float a=1.f)
void setNoise(float strength, float frequency=1.f, float speed=1.f)
void attachToSpineBoneByName(animation::SpineSkeleton *spine, const std::string &boneName)
void setShader(graphics::Shader *shader)
void setSizeVariation(float variation)
void attachToBoneByName(animation::AnimPose *pose, animation::AnimSkeleton *skeleton, const std::string &boneName)
void setSimulationSpace(const std::string &space)
void setAttachOffset(float x, float y, float z)
void setLights(bool enabled, float radius=120.f, float intensity=1.f, float r=1.f, float g=1.f, float b=1.f, int maxLights=4)
void setParticleLifetime(float minLife, float maxLife)
void addBurst(float time, int count)
void setAttachPlane(const std::string &plane)
void setInheritVelocity(float fraction)
void setParticleSize(float width, float height)
Multi-stop color gradient sampled with linear interpolation between stops. Stops are kept sorted by t...
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition AnimTrail.h:6
glm::vec4 Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Definition Color.h:13
BlendMode
2D quad blend mode (drawn in draw order within a layer).
Definition BlendMode.h:6
bool stepEmitterSimGpu(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim, ParticleEmitter::GpuSim &gpu, float dt)
GPU-accelerated integration step; false = unavailable, caller falls back to CPU.
void spawnParticleAt(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim, float x, float y)
void setWorldCollisionResolver(WorldCollisionFn fn)
bool(*)(float x, float y, float radius, float &nx, float &ny) WorldCollisionFn
World collision query used by emitters with worldCollision enabled.
void spawnParticle(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim)
void stepEmitterSim(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim, float dt)
WorldCollisionFn getWorldCollisionResolver()
bool sampleSkinSpawn(ParticleEmitter::SkinSource &skinSrc, ParticleEmitter::Sim &sim, float &outX, float &outY)
void syncEmitterSources(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &, ParticleEmitter::Attach &attach, ParticleEmitter::SkinSource &skinSrc)
Sync bone attach + refresh skin cache; call before stepEmitterSim when using Attach/SkinSource.
Optional bone attachment. When enabled, syncAttach() writes Config.x/y (and optionally direction) fro...
std::string plane
"xy" | "xz" | "yz" — axes mapped to particle plane (3D sources).
Timed burst emission (fired once while the emitter is active).
Radial attract/repel force fields (strength > 0 attract, < 0 repel).
Emitter-level 2D light emission (pooled Light2D entities).
Script-linked sub-emitters (birth / death / collision triggers).
float limitVelocity
Max speed; 0 = unlimited. Applied after forces each step.
float prewarmSeconds
Prewarm seconds simulated in start() so the effect is pre-filled.
int hframes
Flipbook grid. 1x1 = static full texture. frameRate = frames/sec (0 = static).
float maxDeltaTime
Cap per-step delta time (0 = unlimited).
float noiseStrength
Turbulence: random per-particle acceleration scaled by strength.
bool gpuSimulation
Opt-in GPU-accelerated simulation (falls back to CPU when unavailable).
float frameRandomStart
0..1 fraction: randomize the starting frame up to this fraction of the sheet.
std::string simSpace
"world" (default) or "local" (particles track the emitter).
ParticleCurve rotationCurve
Optional extra rotation (degrees) over lifetime; added on top of spin.
ParticleCurve sizeCurve
Optional size scale curve over lifetime; overrides sizeStart/sizeEnd.
float startRotMin
Initial rotation in degrees (random between min/max; radians at sim time).
std::string overflowMode
Buffer-full strategy: "drop" (default) | "pause" | "warn".
ParticleCurve velocityCurve
Optional speed multiplier curve over lifetime.
ParticleGradient colorGradient
Optional multi-stop gradient; overrides colorStart/colorEnd when non-empty.
std::string areaType
"none" | "ellipse" | "rect" (≤15).
struct eve::particles::ParticleEmitter::Config::LightCfg lights
float inheritVelocity
Fraction [0,1] of the emitter's current velocity added to new particles.
float damping
Per-second velocity damping fraction in [0,1].
float gravityX
Gravity applied every step (world units/s²).
bool worldCollision
Query the engine-level world collision resolver each step.
std::string collisionMode
"none" | "kill" | "bounce" | "stop" on collision.
std::string renderMode
"billboard" (default) | "stretched" (elongate along velocity).
GPU-accelerated simulation state (see ParticleGpuKernel.h for layout).
std::shared_ptr< eve::gpgpu::GpuBuffer > buffer
Pooled Light2D entities driven by ParticleLightSystem (lights.enabled).
std::vector< graphics::Light2D * > pool
Bound config file for hot reload (empty path = unbound).
Optional skinned-mesh surface source. When enabled, newly spawned particles sample random (optionally...
std::vector< int > candidates
Vertex indices eligible for sampling (rebuilt when filter changes).
Single live particle (CPU simulation).
float noisePhase
Random noise phase per particle (turbulence offset).
float frame
Flipbook frame progress (float frame index; grid resolved at render).