载入中...
搜索中...
未找到
Model3D.cpp
浏览该文件的文档.
1#include "model3d/Model3D.h"
4
5#include "common/AsyncWork.h"
6#include "common/Data.h"
7#include "common/Exception.h"
8#include "common/Resource.h"
10#include "filesystem/FileData.h"
12#include "graphics/Graphics.h"
13#include "image/ImageData.h"
14
15#include "medialoader/Exception.h"
16#include "medialoader/model/ModelLoader.h"
17
18#include "model3d/ModelLoader.h"
19
20#include <simplesquirrel/simplesquirrel.hpp>
21
22#include <cctype>
23#include <cstdint>
24#include <cstring>
25#include <functional>
26#include <memory>
27#include <vector>
28
29namespace eve {
30namespace model3d {
31
33
34Model3D::Model3D() = default;
35Model3D::~Model3D() = default;
36
37namespace {
38
39std::string lowerExt(std::string ext) {
40 for (char &c : ext)
41 c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
42 return ext;
43}
44
45std::string ensureDotExt(std::string ext) {
46 ext = lowerExt(std::move(ext));
47 if (ext.empty())
48 return {};
49 if (ext[0] != '.')
50 ext.insert(ext.begin(), '.');
51 return ext;
52}
53
54medialoader::LoadOptions toMedialoader(const ModelLoadOptions &opt) {
55 medialoader::LoadOptions m;
56 m.triangulate = opt.triangulate;
57 m.generateNormalsIfMissing = opt.generateNormalsIfMissing;
58 m.joinIdenticalVertices = opt.joinIdenticalVertices;
59 m.flipUVs = opt.flipUVs;
60 m.improveCacheLocality = opt.improveCacheLocality;
61 return m;
62}
63
64medialoader::ModelScene loadOrThrow(medialoader::ModelLoader &loader, const void *data, size_t size,
65 const char *hint, const medialoader::LoadOptions &opt) {
66 try {
67 return loader.loadFromMemory(data, size, hint, opt);
68 } catch (const medialoader::Exception &e) {
69 throw eve::Exception("%s", e.what());
70 }
71}
72
73constexpr char kEvModelMagic[4] = {'E', 'V', 'M', '1'};
74
75bool unpackEvModel(const void *data, size_t size, const uint8_t **payload, size_t *payloadSize,
76 std::string *hint) {
77 if (!data || size < 6 || std::memcmp(data, kEvModelMagic, 4) != 0) return false;
78 const auto *bytes = static_cast<const uint8_t *>(data);
79 const uint16_t n = uint16_t(bytes[4]) | (uint16_t(bytes[5]) << 8u);
80 if (n == 0 || size < size_t(6u + n)) return false;
81 hint->assign(reinterpret_cast<const char *>(bytes + 6), n);
82 *payload = bytes + 6 + n;
83 *payloadSize = size - 6 - n;
84 return *payloadSize > 0;
85}
86
87} // namespace
88
89ModelData *Model3D::newModelData(Data *data, std::string hintExt) {
90 return newModelData(data, std::move(hintExt), ModelLoadOptions{});
91}
92
93ModelData *Model3D::newModelData(Data *data, std::string hintExt,
95 if (data == nullptr || data->getData() == nullptr || data->getSize() == 0)
96 throw eve::Exception("Cannot decode empty model data");
97
98 std::string hint = ensureDotExt(std::move(hintExt));
99 if (hint.empty()) {
100 if (auto *fd = dynamic_cast<filesystem::FileData *>(data))
101 hint = ensureDotExt(fd->getExtension());
102 }
103
104 const void *encoded = data->getData();
105 size_t encodedSize = data->getSize();
106 if (hint == ".evmodel") {
107 const uint8_t *payload = nullptr;
108 std::string packedHint;
109 if (!unpackEvModel(encoded, encodedSize, &payload, &encodedSize, &packedHint))
110 throw eve::Exception("Invalid or truncated .evmodel envelope");
111 encoded = payload;
112 hint = ensureDotExt(std::move(packedHint));
113 }
114
115 if (hint == ".vrm") hint = ".glb";
116
117 // Prefer VFS for sidecar resolution when FileData carries a filename.
118 filesystem::Filesystem *fs = ModuleManager::getInstance<filesystem::Filesystem>("Filesystem");
119 if (!fs)
120 fs = filesystem::Filesystem::create();
121
122 EveFileSystem eveFs(fs);
123 medialoader::ModelLoader loader(&eveFs);
124
125 auto scene = loadOrThrow(loader, encoded, encodedSize, hint.c_str(),
126 toMedialoader(options));
127 if (scene.empty())
128 throw eve::Exception("Could not decode model data");
129
130 std::string uri;
131 if (auto *fd = dynamic_cast<filesystem::FileData *>(data))
132 uri = "file://" + fd->getFilename();
133
134 return new ModelData(std::move(scene), std::move(uri), options.flipUVs);
135}
136
140
144
146 if (path.empty())
148 "Model path must not be empty", "model3d.prefetch"));
149 if (!eve::cap::query<eve::caps::IAsyncWorkExecutor>())
151 eve::DiagnosticCode::Unsupported, "Model prefetch requires the thread executor", "model3d.prefetch"));
152 if (!ModuleManager::getInstance<filesystem::Filesystem>("Filesystem"))
154 eve::DiagnosticCode::Unsupported, "Initialize Filesystem on the game thread before model prefetch",
155 "model3d.prefetch"));
157}
158
160 if (path.empty())
161 throw eve::Exception("Model3D::newModelDataFromFile: empty path");
162
163 // Route through the unified resource cache: options are part of the key,
164 // so identical (path, options) requests share one decoded Assimp scene.
165 const std::string key = modelCacheKey(path, options);
167 if (!resource)
168 throw eve::Exception("Could not load model: %s", path.c_str());
169 return static_cast<ModelData *>(resource);
170}
171
173 int meshIndex) {
174 if (!gfx)
175 throw eve::Exception("Model3D::createRenderable: null Graphics");
176 if (!model)
177 throw eve::Exception("Model3D::createRenderable: null ModelData");
178 return buildRenderable(*gfx, model, meshIndex);
179}
180
182 graphics::Renderable3D *renderable) {
183 if (!model || !renderable)
185 eve::DiagnosticCode::InvalidArgument, "model and renderable are required", "model3d.foliage-deformation"));
186 return model3d::prepareFoliageDeformation(*model, meshIndex, *renderable);
187}
188
189bool Model3D::bakeModel(const std::string &sourcePath, const std::string &destinationPath) {
190 if (sourcePath.empty() || destinationPath.empty()) return false;
191 filesystem::Filesystem *fs = ModuleManager::getInstance<filesystem::Filesystem>("Filesystem");
192 if (!fs) fs = filesystem::Filesystem::create();
193 filesystem::FileData *sourceRaw = fs->read(sourcePath);
194 if (!sourceRaw) return false;
196 if (!source->getData() || source->getSize() == 0) return false;
197 const std::string hint = ensureDotExt(source->getExtension());
198 if (hint.empty() || hint == ".evmodel" || hint.size() > 65535u) return false;
199 std::vector<uint8_t> packed;
200 packed.reserve(6u + hint.size() + source->getSize());
201 packed.insert(packed.end(), kEvModelMagic, kEvModelMagic + 4);
202 const uint16_t n = static_cast<uint16_t>(hint.size());
203 packed.push_back(static_cast<uint8_t>(n & 0xffu));
204 packed.push_back(static_cast<uint8_t>((n >> 8u) & 0xffu));
205 packed.insert(packed.end(), hint.begin(), hint.end());
206 const auto *bytes = static_cast<const uint8_t *>(source->getData());
207 packed.insert(packed.end(), bytes, bytes + source->getSize());
208 try {
209 fs->write(destinationPath, packed.data(), static_cast<int64_t>(packed.size()));
210 return true;
211 } catch (...) {
212 return false;
213 }
214}
215
216void Model3D::expose(ssq::Table &table) {
217 auto cls = table.addClass(name, Model3D::create, false);
218 expose(cls);
219
220 auto md = table.addClass<ModelData>(
221 "ModelData", std::function<ModelData *()>([]() -> ModelData * { return nullptr; }), true);
222 md.addFunc("empty", &ModelData::empty);
223 md.addFunc("getMeshCount", &ModelData::getMeshCount);
224 md.addFunc("getMaterialCount", &ModelData::getMaterialCount);
225 md.addFunc("getVertexCount", &ModelData::getVertexCount);
226 md.addFunc("getFaceCount", &ModelData::getFaceCount);
227 md.addFunc("getVertexPosition", &ModelData::getVertexPosition);
228 md.addFunc("getVertexNormal", &ModelData::getVertexNormal);
229 md.addFunc("setVertexNormal",
230 [](ModelData *self, int meshIndex, int vertexIndex, float x, float y, float z) {
231 auto result = self->setVertexNormal(meshIndex, vertexIndex, x, y, z);
232 if (!result.ok()) throw eve::Exception("%s", result.status().describe().c_str());
233 });
234 md.addFunc("applyVertexNormals", [](ModelData *self, int meshIndex, std::string kind) {
235 auto result = self->applyVertexNormals(meshIndex, kind);
236 if (!result.ok()) throw eve::Exception("%s", result.status().describe().c_str());
237 });
238 md.addFunc("applyVertexNormalsFrom",
239 [](ModelData *self, int meshIndex, std::string kind, float x, float y, float z) {
240 auto result = self->applyVertexNormalsFrom(meshIndex, kind, x, y, z);
241 if (!result.ok()) throw eve::Exception("%s", result.status().describe().c_str());
242 });
243 md.addFunc("bakeNormalMap",
244 [](ModelData *self, int meshIndex, int width, int height, int uvChannel,
245 std::string space) -> image::ImageData * {
246 auto baked = self->bakeNormalMap(meshIndex, width, height, uvChannel, space);
247 if (!baked.ok()) throw eve::Exception("%s", baked.status().describe().c_str());
248 return std::move(baked).takeValue().release();
249 });
250 md.addFunc("getFaceVertexIndex", &ModelData::getFaceVertexIndex);
251 md.addFunc("hasNormals", &ModelData::hasNormals);
252 md.addFunc("hasTexCoords", &ModelData::hasTexCoords);
253 md.addFunc("getTexCoordChannelCount", &ModelData::getTexCoordChannelCount);
254 md.addFunc("hasTexCoordChannel", &ModelData::hasTexCoordChannel);
255 md.addFunc("getTexCoord", &ModelData::getTexCoord);
256 auto surfaceUv = table.addClass<SurfaceUv>(
257 "SurfaceUv", std::function<SurfaceUv *()>([]() { return new SurfaceUv(); }), true);
258 surfaceUv.addFunc("getU", [](SurfaceUv *value) { return value->u; });
259 surfaceUv.addFunc("getV", [](SurfaceUv *value) { return value->v; });
260 surfaceUv.addFunc("getBarycentricA", [](SurfaceUv *value) { return value->barycentricA; });
261 surfaceUv.addFunc("getBarycentricB", [](SurfaceUv *value) { return value->barycentricB; });
262 surfaceUv.addFunc("getBarycentricC", [](SurfaceUv *value) { return value->barycentricC; });
263 surfaceUv.addFunc("getTriangleIndex", [](SurfaceUv *value) { return value->triangleIndex; });
264 surfaceUv.addFunc("getUvChannel", [](SurfaceUv *value) { return value->uvChannel; });
265 md.addFunc("mapSurfacePointToUv",
266 [](ModelData *self, int meshIndex, int triangleIndex, float x, float y,
267 float z, int channel) -> SurfaceUv * {
268 auto mapped = self->mapSurfacePointToUv(meshIndex, triangleIndex, x, y, z,
269 channel);
270 if (!mapped.ok()) throw eve::Exception("%s", mapped.status().describe().c_str());
271 return new SurfaceUv(std::move(mapped).takeValue());
272 });
273 md.addFunc("hasTangents", &ModelData::hasTangents);
274 md.addFunc("getTangent", &ModelData::getTangent);
275 md.addFunc("getBitangent", &ModelData::getBitangent);
276 md.addFunc("getVertexColorChannelCount", &ModelData::getVertexColorChannelCount);
277 md.addFunc("hasVertexColorChannel", &ModelData::hasVertexColorChannel);
278 md.addFunc("getVertexColor", &ModelData::getVertexColor);
279 md.addFunc("getMaterialIndex", &ModelData::getMaterialIndex);
280 md.addFunc("getMaterialName", &ModelData::getMaterialName);
281 md.addFunc("getMaterialBaseColorR", &ModelData::getMaterialBaseColorR);
282 md.addFunc("getMaterialBaseColorG", &ModelData::getMaterialBaseColorG);
283 md.addFunc("getMaterialBaseColorB", &ModelData::getMaterialBaseColorB);
284 md.addFunc("getMaterialBaseColorA", &ModelData::getMaterialBaseColorA);
285 md.addFunc("getMaterialMetallicFactor", &ModelData::getMaterialMetallicFactor);
286 md.addFunc("getMaterialRoughnessFactor", &ModelData::getMaterialRoughnessFactor);
287 md.addFunc("getMaterialOpacity", &ModelData::getMaterialOpacity);
288 md.addFunc("getMaterialTwoSided", &ModelData::getMaterialTwoSided);
289 md.addFunc("getMaterialAlphaMode", &ModelData::getMaterialAlphaMode);
290 md.addFunc("getMaterialAlphaCutoff", &ModelData::getMaterialAlphaCutoff);
291 md.addFunc("getMaterialTextureSlotCount", &ModelData::getMaterialTextureSlotCount);
292 md.addFunc("getMaterialTexturePath", &ModelData::getMaterialTexturePath);
293 md.addFunc("getMaterialTextureEmbeddedIndex", &ModelData::getMaterialTextureEmbeddedIndex);
294 md.addFunc("getEmbeddedTextureCount", &ModelData::getEmbeddedTextureCount);
295 md.addFunc("getEmbeddedTextureName", &ModelData::getEmbeddedTextureName);
296 md.addFunc("getEmbeddedTextureWidth", &ModelData::getEmbeddedTextureWidth);
297 md.addFunc("getEmbeddedTextureHeight", &ModelData::getEmbeddedTextureHeight);
298 md.addFunc("getEmbeddedTextureImageData", &ModelData::getEmbeddedTextureImageData);
299 md.addFunc("getMorphTargetCount", &ModelData::getMorphTargetCount);
300 md.addFunc("getMorphTargetName", &ModelData::getMorphTargetName);
301 md.addFunc("hasBones", &ModelData::hasBones);
302 md.addFunc("getBoneCount", &ModelData::getBoneCount);
303 md.addFunc("getBoneName", &ModelData::getBoneName);
304 md.addFunc("getInverseBindMatrixElement", &ModelData::getInverseBindMatrixElement);
305 md.addFunc("getBoneWeightCount", &ModelData::getBoneWeightCount);
306 md.addFunc("getBoneWeightVertex", &ModelData::getBoneWeightVertex);
307 md.addFunc("getBoneWeightValue", &ModelData::getBoneWeightValue);
308 md.addFunc("getAnimationCount", &ModelData::getAnimationCount);
309 md.addFunc("getAnimationName", &ModelData::getAnimationName);
310}
311
312void Model3D::expose(ssq::Class &cls) {
313 const auto parseOptions = [](ssq::Object object) {
315 if (!value || !value.value().isObject()) throw eve::Exception("Expected model decode options table");
316 ModelLoadOptions options;
317 for (const auto &key : value.value().keys()) {
318 const auto *field = value.value().find(key);
319 if (!field->isBool()) throw eve::Exception("Model decode options must be booleans");
320 const bool flag = field->asBool();
321 if (key == "triangulate")
322 options.triangulate = flag;
323 else if (key == "generateNormalsIfMissing")
324 options.generateNormalsIfMissing = flag;
325 else if (key == "joinIdenticalVertices")
326 options.joinIdenticalVertices = flag;
327 else if (key == "flipUVs")
328 options.flipUVs = flag;
329 else if (key == "improveCacheLocality")
330 options.improveCacheLocality = flag;
331 else
332 throw eve::Exception("Unknown model decode option: %s", key.c_str());
333 }
334 return options;
335 };
336 cls.addFunc("requestModelDataWithOptions",
337 [vm = cls.getHandle(), parseOptions](Model3D *self, const std::string &path, ssq::Object object) {
338 try {
339 return eve::script::projectResult(vm, self->requestModelData(path, parseOptions(object)));
340 } catch (const std::exception &error) {
343 error.what(), "model3d.prefetch")));
344 }
345 });
346 cls.addFunc("loadModelDataWithOptions", [vm = cls.getHandle(), parseOptions](Model3D *self, const std::string &path,
347 ssq::Object object) {
348 try {
349 auto options = parseOptions(object);
350 auto *model = self->newModelDataFromFile(path, options);
352 projected.set("value", model);
354 projected.set("ownership", std::string("borrowed-from-resource-cache"));
355 return projected;
356 } catch (const std::exception &error) {
360 }
361 });
362 cls.addFunc("requestModelData", [vm = cls.getHandle()](Model3D *self, const std::string &path) {
363 return eve::script::projectResult(vm, self->requestModelData(path));
364 });
365 cls.addFunc("getName", &Model3D::getName);
366 cls.addFunc("newModelData", static_cast<ModelData *(Model3D::*)(Data *, std::string)>(
368 cls.addFunc("newModelDataFromFile",
369 static_cast<ModelData *(Model3D::*)(std::string)>(&Model3D::newModelDataFromFile));
370 cls.addFunc("createRenderable", &Model3D::createRenderable);
371 cls.addFunc("prepareFoliageDeformation", [vm = cls.getHandle()](Model3D *self, ModelData *model, int meshIndex,
372 graphics::Renderable3D *renderable) {
373 return script::projectResult(vm, self->prepareFoliageDeformation(model, meshIndex, renderable));
374 });
375 cls.addFunc("bakeModel", &Model3D::bakeModel);
376}
377
378} // namespace model3d
379} // namespace eve
double value
Value::Object payload
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
Capability for submitting CPU work onto a worker pool.
Vec3 projected
Definition CaveMesh.cpp:122
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::uint32_t key
glm::vec3 n
Definition Grass.cpp:63
std::int32_t c
std::uint32_t height
std::uint32_t width
TokenKind kind
std::uint64_t bytes
std::string name
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::string error
Definition Package.cpp:60
std::string path
Definition PlayHost.cpp:110
std::string uri
std::weak_ptr< PrimitiveScene > scene
glm::mat4 model
std::string resource
const SquirrelValueOptions & options
The single Squirrel projection for common Result, Status and Value.
std::string sourcePath
Json object
float size
Definition TreeMesh.cpp:156
const UnitySourceAsset & source
float m[16]
Data public API.
Definition Data.h:8
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
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
virtual std::string getName() const =0
Returns the name.
eve::Result< void > request(std::string key)
Queue a cache miss load without blocking for decode to finish.
Definition Resource.cpp:149
Resource * get(std::string key)
Get the cached resource for key; on a miss, load it through the registered IAssetReloader providers a...
Definition Resource.cpp:141
static ResourceManager & getInstance()
Returns the instance.
Definition Resource.cpp:15
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 failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status failure(StatusCode code, Diagnostic diagnostic)
Construct a failed status with one diagnostic.
Definition Status.h:84
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Data buffer paired with a filename (used for type identification).
Definition FileData.h:14
EVENGINE_API_FOUNDATION public API.
Definition Filesystem.h:43
virtual void write(std::string filename, const void *data, int64_t size) const =0
Write data to a file.
virtual FileData * read(std::string filename, int64_t size=File::ALL) const =0
Reads data from a file.
EVENGINE_API_BACKENDS public API.
medialoader::FileSystem adapter over eve::filesystem (physfs / VFS).
Resource module for decoding 3D models via medialoader (Assimp). Produces ModelData; GPU upload is gr...
Definition Model3D.h:43
ModelData * newModelData(Data *data, std::string hintExt="")
Decode model from in-memory bytes.
Definition Model3D.cpp:89
graphics::Renderable3D * createRenderable(graphics::Graphics *gfx, ModelData *model, int meshIndex)
Creates renderable.
Definition Model3D.cpp:172
ModelData * newModelDataFromFile(std::string path)
Decode model from a VFS path (physfs). Uses EveFileSystem so sidecar files (.mtl, textures) resolve t...
Definition Model3D.cpp:137
~Model3D() override
Model 3 d.
bool bakeModel(const std::string &sourcePath, const std::string &destinationPath)
Pack a self-contained source model (normally GLB or embedded FBX) into EVEngine's deterministic ....
Definition Model3D.cpp:189
eve::Result< void > prepareFoliageDeformation(ModelData *model, int meshIndex, graphics::Renderable3D *renderable)
Prepare one default-imported renderable for native PBR vegetation motion.
Definition Model3D.cpp:181
eve::Result< void > requestModelData(const std::string &path)
Queue default-option model decoding on the engine resource worker pool.
Definition Model3D.cpp:141
CPU-side decoded 3D model (Assimp scene owned via medialoader::ModelScene). Does not upload to GPU — ...
Definition ModelData.h:39
bool empty() const
Empty.
Definition ModelData.cpp:65
int getEmbeddedTextureCount() const
Returns the embedded texture count.
int getMeshCount() const
Returns the mesh count.
Definition ModelData.cpp:91
float getMaterialAlphaCutoff(int matIndex) const
Returns the material alpha cutoff.
float getMaterialBaseColorR(int matIndex) const
Base color: glTF BASE_COLOR, falling back to OBJ/legacy DIFFUSE.
float getMaterialMetallicFactor(int matIndex) const
PBR factors; defaults 0 (metallic) / 0.45 (roughness) when absent.
float getMaterialBaseColorB(int matIndex) const
Returns the material base color b.
float getMaterialBaseColorG(int matIndex) const
Returns the material base color g.
float getVertexPosition(int meshIndex, int vertexIndex, int component) const
Source position component for one vertex (component 0=x, 1=y, 2=z).
std::string getMaterialAlphaMode(int matIndex) const
glTF alpha mode normalized to "OPAQUE", "MASK", or "BLEND".
bool hasVertexColorChannel(int meshIndex, int channel) const
Whether a mesh has the requested vertex-color channel.
int getFaceCount(int meshIndex) const
Returns the face count.
bool hasNormals(int meshIndex) const
True when normals.
float getVertexColor(int meshIndex, int channel, int vertexIndex, int component) const
RGBA component (0..3) in a vertex-color channel.
int getEmbeddedTextureHeight(int idx) const
0 means a compressed blob (use getEmbeddedTextureImageData to decode).
int getBoneCount(int meshIndex) const
Returns the bone count.
int getBoneWeightCount(int meshIndex, int boneIndex) const
Returns the bone weight count.
float getTexCoord(int meshIndex, int channel, int vertexIndex, int component) const
UV component in channel for vertex (component 0=u, 1=v, 2=w).
float getMaterialOpacity(int matIndex) const
Returns the material opacity.
std::string getMaterialTexturePath(int matIndex, const std::string &type, int slot=0) const
External file path, or "*N" for an embedded texture. Empty when absent.
int getMaterialTextureSlotCount(int matIndex, const std::string &type) const
Texture type names (Squirrel strings): "base_color", "diffuse", "normals", "height",...
float getInverseBindMatrixElement(int meshIndex, int boneIndex, int elementIndex) const
Inverse-bind (offset) matrix element, column-major, elementIndex in [0,15].
std::string getMaterialName(int matIndex) const
Returns the material name.
float getTangent(int meshIndex, int vertexIndex, int component) const
Tangent XYZ component for a vertex.
int getEmbeddedTextureWidth(int idx) const
Returns the embedded texture width.
bool getMaterialTwoSided(int matIndex) const
Returns the material two sided.
int getAnimationCount() const
Scene-level animation clips (aiAnimation).
bool hasTangents(int meshIndex) const
Whether imported tangent and bitangent streams exist.
int getTexCoordChannelCount(int meshIndex) const
Number of populated UV channels (0..AI_MAX_NUMBER_OF_TEXTURECOORDS).
float getBitangent(int meshIndex, int vertexIndex, int component) const
Bitangent XYZ component for a vertex.
float getVertexNormal(int meshIndex, int vertexIndex, int component) const
Source normal component for one vertex (component 0=x, 1=y, 2=z).
float getMaterialBaseColorA(int matIndex) const
Returns the material base color a.
bool hasTexCoordChannel(int meshIndex, int channel) const
Whether a mesh has the requested UV channel.
float getBoneWeightValue(int meshIndex, int boneIndex, int weightIndex) const
Returns the bone weight value.
std::string getMorphTargetName(int meshIndex, int morphIndex) const
Returns the morph target name.
std::string getAnimationName(int animIndex) const
Returns the animation name.
int getVertexColorChannelCount(int meshIndex) const
Number of populated vertex-color channels.
bool hasTexCoords(int meshIndex) const
True when tex coords.
int getBoneWeightVertex(int meshIndex, int boneIndex, int weightIndex) const
Returns the bone weight vertex.
image::ImageData * getEmbeddedTextureImageData(int idx) const
Returns the embedded texture image data.
int getMaterialCount() const
Returns the material count.
Definition ModelData.cpp:96
int getMaterialIndex(int meshIndex) const
Assimp material slot referenced by a mesh; -1 when invalid.
int getMaterialTextureEmbeddedIndex(int matIndex, const std::string &type, int slot=0) const
Scene texture index for embedded "*N" references; -1 for external files.
float getMaterialRoughnessFactor(int matIndex) const
Returns the material roughness factor.
bool hasBones(int meshIndex) const
Assimp skeletal skin data (aiBone / vertex weights) on a mesh.
int getFaceVertexIndex(int meshIndex, int triangleIndex, int corner) const
Source vertex index for one triangle corner (corner 0..2).
std::string getBoneName(int meshIndex, int boneIndex) const
Returns the bone name.
int getVertexCount(int meshIndex) const
Returns the vertex count.
std::string getEmbeddedTextureName(int idx) const
Returns the embedded texture name.
int getMorphTargetCount(int meshIndex) const
Assimp morph / blend-shape targets on a mesh (aiAnimMesh).
Result< void > prepareFoliageDeformation(ModelData &model, int meshIndex, Renderable3D &renderable)
std::string modelCacheKey(const std::string &path, const ModelLoadOptions &options)
Build a deterministic ResourceManager cache key for path + options.
Renderable3D * buildRenderable(IResourceFactory &gfx, ModelData *model, int meshIndex, const ModelRenderOptions &options)
const EditorValue * field(const EditorValue &value, const char *name)
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
void markResultHasValue(ssq::Table &result)
Declare that a projected table received a payload the caller attached itself.
std::unique_ptr< T > Owned
C++ owning storage used by an object factory.
ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status &status)
Project a checked native status that carries no payload.
ssq::Table projectResult(HSQUIRRELVM vm, Result< void > &&result)
Consume and project a void native Result using the common schema.
Result< Value > valueFromSquirrel(HSQUIRRELVM vm, SQInteger index, const SquirrelValueOptions &options)
Convert one Squirrel value into the canonical owning Value tree.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
Toggles for the Assimp post-processing steps applied by medialoader on decode. All default to on (mat...
Definition Model3D.h:30