载入中...
搜索中...
未找到
DecalAttackVfx.cpp
浏览该文件的文档.
1#include "common/Capability.h"
2#include "common/Module.h"
4#include "graphics/Graphics.h"
5#include "graphics/Texture.h"
7
8#include <cstdint>
9#include <string>
10#include <unordered_map>
11#include <utility>
12
13namespace eve::decal {
14namespace {
15
16float layerParam(const eve::stylize::AttackVfxLayerStartRequest& request, const char* key,
17 float fallback) {
18 if (request.layer) {
19 const auto found = request.layer->floatParams.find(key);
20 if (found != request.layer->floatParams.end()) return found->second;
21 }
22 return fallback;
23}
24
25graphics::Texture* resolveAlbedo(const eve::stylize::AttackVfxLayerStartRequest& request) {
26 auto* graphics = eve::ModuleManager::getInstance<graphics::Graphics>("Graphics");
27 if (!graphics) return nullptr;
28
29 const std::string& uri = request.layer ? request.layer->uri : std::string{};
30 if (uri.rfind("solid:", 0) == 0 || uri.rfind("decal://", 0) == 0 || uri.empty()) {
31 const float r = layerParam(request, "r", 0.85f);
32 const float g = layerParam(request, "g", 0.25f);
33 const float b = layerParam(request, "b", 0.08f);
34 const float a = layerParam(request, "a", 0.85f);
35 const auto toByte = [](float v) -> std::uint8_t {
36 if (v <= 0.f) return 0;
37 if (v >= 1.f) return 255;
38 return static_cast<std::uint8_t>(v * 255.f + 0.5f);
39 };
40 const std::uint8_t rgba[4] = {toByte(r), toByte(g), toByte(b), toByte(a)};
41 return graphics->newTexture(1, 1, rgba);
42 }
43 try {
44 return graphics->newTextureFromFile(uri);
45 } catch (...) {
46 return nullptr;
47 }
48}
49
50class DecalAttackVfxExecutor final : public eve::stylize::IAttackVfxLayerExecutor {
51public:
52 ~DecalAttackVfxExecutor() {
53 // Same atexit hazard as ParticlesAttackVfx: this static may outlive Graphics.
54 // Drop albedo pointers without calling releaseTexture (process is exiting).
55 live_.clear();
56 }
57
58 void clearLive() {
59 for (auto& entry : live_) {
60 if (entry.second.decalId > 0) (void)DecalManager::inst().remove(entry.second.decalId);
61 releaseTexture(entry.second.albedo);
62 entry.second.albedo = nullptr;
63 }
64 live_.clear();
65 }
66
67 eve::stylize::AttackVfxLayerRole role() const noexcept override {
69 }
70
73 if (!request.layer)
75 eve::DiagnosticCode::InvalidArgument, "AttackVfx decal layer is null", "layer"));
76 if (request.layer->uri.empty())
78 eve::DiagnosticCode::InvalidArgument, "AttackVfx decal layer uri is empty", "uri"));
79
80 graphics::Texture* albedo = resolveAlbedo(request);
81 if (!albedo)
83 eve::DiagnosticCode::NotFound, "decal albedo texture could not be resolved", "uri"));
84
85 // World placement comes from authored floatParams (or spatial adapters later).
86 // Entity ids must never be treated as coordinates.
87 const float x = layerParam(request, "x", 0.f);
88 const float y = layerParam(request, "y", 0.05f);
89 const float z = layerParam(request, "z", 0.f);
90 const float size = layerParam(request, "size", 1.2f);
91 const float depth = layerParam(request, "depth", 0.35f);
92 const float lifetime = layerParam(request, "lifetime", 1.5f);
93 const float fadeIn = layerParam(request, "fadeIn", 0.05f);
94 const float fadeOut = layerParam(request, "fadeOut", 0.35f);
95 const int seed =
96 request.playRequest
97 ? static_cast<int>(request.playRequest->sourceId ^ request.playRequest->targetId)
98 : 0;
99
100 const int decalId = DecalManager::inst().project(
101 x, y, z, 0.f, 1.f, 0.f, albedo, "attackvfx", size, depth, true, seed, fadeIn, lifetime,
102 fadeOut, 0.f, 0.f, 0.f, 0.f);
103 if (decalId <= 0) {
104 releaseTexture(albedo);
106 eve::DiagnosticCode::Failed, "DecalManager.project returned an invalid id", "uri"));
107 }
108
109 Live owned;
110 owned.decalId = decalId;
111 owned.albedo = albedo;
112 const auto id = ++nextId_;
113 live_.emplace(id, std::move(owned));
116 }
117
120 const auto found = live_.find(handle.id);
121 if (found == live_.end())
123 eve::DiagnosticCode::StaleHandle, "decal AttackVfx layer handle is stale", "handle"));
124
125 // Coalesce DecalManager::update to once per runtime tickSerial.
126 if (request.tickSerial != lastTickSerial_) {
127 DecalManager::inst().update(static_cast<float>(dtSeconds));
128 lastTickSerial_ = request.tickSerial;
129 }
130
131 if (found->second.draining && !DecalManager::inst().contains(found->second.decalId)) {
132 releaseTexture(found->second.albedo);
133 live_.erase(found);
135 }
137 }
138
140 eve::stylize::AttackVfxStopBehavior behavior) override {
141 const auto found = live_.find(handle.id);
142 if (found == live_.end())
144 eve::DiagnosticCode::StaleHandle, "decal AttackVfx layer handle is stale", "handle"));
146 (void)DecalManager::inst().remove(found->second.decalId);
147 releaseTexture(found->second.albedo);
148 live_.erase(found);
150 }
151 // StopEmitting: leave the projected decal to age out via update()/DecalManager.
152 found->second.draining = true;
154 }
155
156private:
157 struct Live {
158 int decalId = 0;
159 graphics::Texture* albedo = nullptr;
160 bool draining = false;
161 };
162
163 static void releaseTexture(graphics::Texture* texture) {
164 if (!texture) return;
165 auto* graphics = eve::ModuleManager::getInstance<graphics::Graphics>("Graphics");
166 if (graphics && graphics->releaseTexture(texture)) delete texture;
167 }
168
169 std::uint64_t nextId_ = 1;
170 std::uint64_t lastTickSerial_ = 0;
171 std::unordered_map<std::uint64_t, Live> live_;
172};
173
174DecalAttackVfxExecutor& executor() {
175 static DecalAttackVfxExecutor instance;
176 return instance;
177}
178
179bool gRegistered = false;
180
181} // namespace
182
184 if (gRegistered) return;
185 eve::cap::addListener<eve::stylize::IAttackVfxLayerExecutor>(&executor());
186 gRegistered = true;
187}
188
190 if (!gRegistered) return;
191 eve::cap::removeListener<eve::stylize::IAttackVfxLayerExecutor>(&executor());
192 executor().clearLive();
193 gRegistered = false;
194}
195
196} // namespace eve::decal
Duration start
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
Pluggable AttackVfx layer backends registered via capability listeners.
ActiveSource owned
bool draining
int decalId
graphics::Texture * albedo
const GltfImportRequest & request
tensor::Graph g
Definition GpuGraph.cpp:7
std::uint32_t key
double r
float v
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::weak_ptr< const void > lifetime
std::uint32_t seed
Definition PointSet.cpp:807
std::string uri
PrimitiveHandle handle
bool found
float size
Definition TreeMesh.cpp:156
std::uint32_t depth
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
bool contains(int id) const
Whether a projected decal id is still registered.
static DecalManager & inst()
Inst.
void update(float dt)
Advance instance ages; expire and evict finished decals.
bool remove(int id)
Removes .
int project(float x, float y, float z, float nx, float ny, float nz, graphics::Texture *albedo, const std::string &kind, float size, float depth, bool randomYaw, int seed, float fadeIn, float lifetime, float fadeOut, float normalStrength, float roughnessStrength, float metalStrength, float emissiveStrength)
Spawn a decal at a world-space point facing along (nx,ny,nz).
Optional open backend for one AttackVfxLayerRole.
void unregisterDecalAttackVfxExecutor()
Unregister the AttackVfx Decal layer executor.
void registerDecalAttackVfxExecutor()
Register the AttackVfx Decal layer executor (soft-skips when Graphics is absent).
AttackVfxLayerRole
Which existing presentation backend a layer binds to.
AttackVfxStopBehavior
Exit policy when a phase or instance stops.
Opaque executor-owned layer instance identity.
Borrowed start context for one layer inside an entering phase.