载入中...
搜索中...
未找到
ModelLoader.cpp
浏览该文件的文档.
1// Loads ModelData through the unified resource cache (common/Resource.h).
2//
3// The cache key serializes ModelLoadOptions, so the same file decoded with
4// different options is distinct cached resources while identical requests
5// share one decoded Assimp scene.
6
8
10#include "common/Capability.h"
11#include "common/Exception.h"
12#include "common/Module.h"
13#include "common/Resource.h"
15#include "filesystem/FileData.h"
16#include "medialoader/Exception.h"
17#include "medialoader/model/ModelLoader.h"
19#include "model3d/Model3D.h"
20#include "model3d/ModelData.h"
21
22#include <cctype>
23#include <cstdlib>
24#include <string>
25
26namespace eve::model3d {
27namespace {
28
29std::string extensionOf(const std::string &path) {
30 const auto pos = path.find_last_of('.');
31 if (pos == std::string::npos) return {};
32 std::string ext = path.substr(pos);
33 for (char &c : ext) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
34 return ext;
35}
36
37bool isModelPath(const std::string &path) {
38 const std::string ext = extensionOf(path);
39 return ext == ".obj" || ext == ".fbx" || ext == ".gltf" || ext == ".glb" || ext == ".vrm" || ext == ".dae" ||
40 ext == ".3ds" || ext == ".blend" || ext == ".stl" || ext == ".ply" || ext == ".x" || ext == ".lwo" ||
41 ext == ".lws" || ext == ".md5mesh" || ext == ".md5anim" || ext == ".b3d" || ext == ".csm" || ext == ".irr" ||
42 ext == ".irrmesh" || ext == ".md2" || ext == ".md3" || ext == ".ms3d" || ext == ".smd" || ext == ".vta" ||
43 ext == ".bvh" || ext == ".ac" || ext == ".off" || ext == ".raw" || ext == ".ter" || ext == ".nff" ||
44 ext == ".ndo" || ext == ".evmodel";
45}
46
47// Mirror of the option mapping in Model3D.cpp.
48medialoader::LoadOptions toMedialoader(const ModelLoadOptions &opt) {
49 medialoader::LoadOptions m;
50 m.triangulate = opt.triangulate;
51 m.generateNormalsIfMissing = opt.generateNormalsIfMissing;
52 m.joinIdenticalVertices = opt.joinIdenticalVertices;
53 m.flipUVs = opt.flipUVs;
54 m.improveCacheLocality = opt.improveCacheLocality;
55 return m;
56}
57
58int queryInt(const std::string &key, const std::string &name, int fallback) {
59 const auto q = key.find('?');
60 if (q == std::string::npos) return fallback;
61
62 std::string rest = key.substr(q + 1);
63 size_t pos = 0;
64 while (pos < rest.size()) {
65 const size_t amp = rest.find('&', pos);
66 const std::string pair =
67 rest.substr(pos, amp == std::string::npos ? std::string::npos : amp - pos);
68 const auto eq = pair.find('=');
69 if (eq != std::string::npos && pair.substr(0, eq) == name)
70 return std::atoi(pair.c_str() + eq + 1);
71 if (amp == std::string::npos) break;
72 pos = amp + 1;
73 }
74 return fallback;
75}
76
77bool queryBool(const std::string &key, const std::string &name, bool fallback) {
78 return queryInt(key, name, fallback ? 1 : 0) != 0;
79}
80
81ModelLoadOptions parseOptions(const std::string &key) {
82 ModelLoadOptions o;
83 o.triangulate = queryBool(key, "triangulate", o.triangulate);
84 o.generateNormalsIfMissing = queryBool(key, "normals", o.generateNormalsIfMissing);
85 o.joinIdenticalVertices = queryBool(key, "join", o.joinIdenticalVertices);
86 o.flipUVs = queryBool(key, "flipuv", o.flipUVs);
87 o.improveCacheLocality = queryBool(key, "cache", o.improveCacheLocality);
88 return o;
89}
90
91class ModelLoader : public eve::caps::IAssetReloader {
92public:
93 const char *reloadKind() const override { return "model"; }
94
95 bool handlesPath(const std::string &normPath) const override {
96 return isModelPath(eve::ResourceManager::pathOfKey(normPath));
97 }
98
104 eve::Result<bool> reload(const std::string &) override { return eve::Result<bool>::success(false); }
105
106 eve::Resource *load(const std::string &key) override {
107 const std::string path = eve::ResourceManager::pathOfKey(key);
108 if (!isModelPath(path)) return nullptr;
109 const ModelLoadOptions options = parseOptions(key);
110
111 filesystem::Filesystem *fs =
112 ModuleManager::getInstance<filesystem::Filesystem>("Filesystem");
113 if (!fs) fs = filesystem::Filesystem::create();
114 if (!fs) return nullptr;
115
116 if (extensionOf(path) == ".evmodel" || extensionOf(path) == ".vrm") {
117 filesystem::FileData *packedRaw = fs->read(path);
118 if (!packedRaw) return nullptr;
120 Model3D *module = ModuleManager::getInstance<Model3D>("Model3D");
121 if (!module) module = Model3D::create();
122 return module->newModelData(packed.get(), extensionOf(path), options);
123 }
124
125 EveFileSystem eveFs(fs);
126 medialoader::ModelLoader loader(&eveFs);
127 try {
128 auto scene = loader.loadFromPath(path.c_str(), toMedialoader(options));
129 if (scene.empty())
130 throw eve::Exception("Could not load model: %s", path.c_str());
131 return new ModelData(std::move(scene), "file://" + path, options.flipUVs);
132 } catch (const medialoader::Exception &e) {
133 throw eve::Exception("%s", e.what());
134 }
135 }
136};
137
138struct Register {
139 Register() {
140 static ModelLoader loader;
141 eve::cap::addListener<eve::caps::IAssetReloader>(&loader, eve::caps::IAssetReloader::kCache);
142 }
143} g_register;
144
145} // namespace
146
147std::string modelCacheKey(const std::string &path, const ModelLoadOptions &options) {
148 std::string query;
149 query += "triangulate=" + std::to_string(options.triangulate ? 1 : 0);
150 query += "&normals=" + std::to_string(options.generateNormalsIfMissing ? 1 : 0);
151 query += "&join=" + std::to_string(options.joinIdenticalVertices ? 1 : 0);
152 query += "&flipuv=" + std::to_string(options.flipUVs ? 1 : 0);
153 query += "&cache=" + std::to_string(options.improveCacheLocality ? 1 : 0);
154 return eve::ResourceManager::makeKey(path, query);
155}
156
157ModelLoadOptions modelOptionsFromKey(const std::string &key) { return parseOptions(key); }
158
159} // namespace eve::model3d
std::uint32_t key
std::array< double, 10 > q
std::int32_t c
std::string name
std::string path
Definition PlayHost.cpp:110
std::weak_ptr< PrimitiveScene > scene
const SquirrelValueOptions & options
float m[16]
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
static std::string pathOfKey(const std::string &key)
The path part of a cache key (everything before the first '?').
Definition Resource.cpp:46
static std::string makeKey(const std::string &path, const std::string &query="")
Build a cache key from a path and optional ?query parameters.
Definition Resource.cpp:37
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Definition Resource.h:51
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.
std::string modelCacheKey(const std::string &path, const ModelLoadOptions &options)
Build a deterministic ResourceManager cache key for path + options.
ModelLoadOptions modelOptionsFromKey(const std::string &key)
Parse the options back out of a model cache key (inverse of modelCacheKey).
std::unique_ptr< T > Owned
C++ owning storage used by an object factory.
Toggles for the Assimp post-processing steps applied by medialoader on decode. All default to on (mat...
Definition Model3D.h:30