载入中...
搜索中...
未找到
ParticleReloader.cpp
浏览该文件的文档.
1// Reloads emitters when their JSON config changes, and re-binds emitter
2// textures when the image they reference changes.
3//
4// Registered at kConsumer priority so graphics has already re-uploaded the
5// texture by the time the emitters pick it up again.
6
8#include "common/Capability.h"
9#include "common/ECS.h"
10#include "common/Module.h"
11#include "graphics/Graphics.h"
14
15#include <cctype>
16#include <string>
17
18namespace eve::particles {
19namespace {
20
21std::string extensionOf(const std::string &path) {
22 const auto pos = path.find_last_of('.');
23 if (pos == std::string::npos) return {};
24 std::string ext = path.substr(pos);
25 for (char &c : ext) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
26 return ext;
27}
28
29bool isImagePath(const std::string &path) {
30 const std::string ext = extensionOf(path);
31 return ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp" || ext == ".tga" ||
32 ext == ".gif" || ext == ".webp" || ext == ".exr" || ext == ".hdr";
33}
34
36std::string normalize(std::string path) {
37 for (char &c : path) {
38 if (c == '\\') c = '/';
39 }
40 while (path.size() >= 2 && path[0] == '.' && path[1] == '/') path.erase(0, 2);
41 while (path.size() > 1 && path.back() == '/') path.pop_back();
42 return path;
43}
44
45class ParticleReloader : public eve::caps::IAssetReloader {
46public:
47 const char *reloadKind() const override { return "particle"; }
48
49 bool handlesPath(const std::string &normPath) const override {
50 const std::string ext = extensionOf(normPath);
51 return ext == ".json" || isImagePath(normPath);
52 }
53
54 eve::Result<bool> reload(const std::string &normPath) override {
55 if (ecs::current()->getManager<ParticleEmitter>() == nullptr) return eve::Result<bool>::success(false);
56 return eve::Result<bool>::success(isImagePath(normPath) ? rebindTexture(normPath) : reloadConfig(normPath));
57 }
58
59private:
60 static bool reloadConfig(const std::string &normPath) {
61 int reloaded = 0;
62 auto view = ecs::View<ParticleEmitter, ParticleEmitter::Config, ParticleEmitter::Resource>();
63 for (auto it = view.begin(); it != view.end(); ++it) {
64 auto [cfg, res] = *it;
65 if (!cfg->entity || res->path.empty()) continue;
66 if (normalize(res->path) != normPath) continue;
67 if (reloadConfigFile(cfg->entity, nullptr)) ++reloaded;
68 }
69 return reloaded > 0;
70 }
71
72 static bool rebindTexture(const std::string &normPath) {
73 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
74 if (!gfx) return false;
75
76 bool any = false;
77 auto view = ecs::View<ParticleEmitter, ParticleEmitter::Config, ParticleEmitter::Resource>();
78 for (auto it = view.begin(); it != view.end(); ++it) {
79 auto [cfg, res] = *it;
80 if (!cfg->entity) continue;
81 const bool albedo = !res->texturePath.empty() && normalize(res->texturePath) == normPath;
82 const bool normal = !res->normalTexturePath.empty() && normalize(res->normalTexturePath) == normPath;
83 if (!albedo && !normal) continue;
84 try {
85 auto* texture = gfx->newTextureFromFile(normPath);
86 if (albedo) cfg->entity->setTexture(texture);
87 if (normal) cfg->entity->setNormalTexture(texture);
88 any = true;
89 } catch (...) {
90 }
91 }
92 return any;
93 }
94};
95
96struct Register {
97 Register() {
98 static ParticleReloader reloader;
99 eve::cap::addListener<eve::caps::IAssetReloader>(&reloader,
101 }
102} g_register;
103
104} // namespace
105} // namespace eve::particles
graphics::Texture * albedo
std::int32_t c
Texture * normal
std::string path
Definition PlayHost.cpp:110
glm::mat4 view
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
EVENGINE_API_FOUNDATION public API.
bool reloadConfigFile(ParticleEmitter *emitter, std::string *error)
Re-read Resource.path if set; updates modtime.
Vec2 normalize(const Vec2 &a)
Normalize.
Definition UrbanTypes.h:44