Wildvine Engine
Referencia Doxygen del codigo propio de Wildvine Engine.
Cargando...
Buscando...
Nada coincide
Model3D.cpp
Ir a la documentación de este archivo.
1
6#include "Model3D.h"
7#include <chrono>
8#include <cstdint>
9#include <cmath>
10#include <fstream>
11#include <unordered_map>
12#include <sstream>
13
14namespace {
15constexpr uint32_t kModelCacheMagic = 0x48564D57; // WMVH
16constexpr uint32_t kModelCacheVersion = 1;
17
19 std::vector<MeshComponent> meshes;
20 std::vector<std::string> textureFileNames;
21};
22
23std::unordered_map<std::string, ModelCacheEntry> g_modelCache;
24
25bool GetFileWriteTime(const std::string& path, ULONGLONG& outWriteTime) {
26 WIN32_FILE_ATTRIBUTE_DATA attributes{};
27 if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attributes)) {
28 return false;
29 }
30
31 ULARGE_INTEGER fileTime{};
32 fileTime.LowPart = attributes.ftLastWriteTime.dwLowDateTime;
33 fileTime.HighPart = attributes.ftLastWriteTime.dwHighDateTime;
34 outWriteTime = fileTime.QuadPart;
35 return true;
36}
37
38bool WriteString(std::ofstream& stream, const std::string& value) {
39 const uint32_t length = static_cast<uint32_t>(value.size());
40 stream.write(reinterpret_cast<const char*>(&length), sizeof(length));
41 if (length > 0) {
42 stream.write(value.data(), length);
43 }
44 return stream.good();
45}
46
47bool ReadString(std::ifstream& stream, std::string& value) {
48 uint32_t length = 0;
49 stream.read(reinterpret_cast<char*>(&length), sizeof(length));
50 if (!stream.good()) {
51 return false;
52 }
53
54 value.resize(length);
55 if (length > 0) {
56 stream.read(&value[0], length);
57 }
58 return stream.good();
59}
60}
61
65
66bool
67Model3D::load(const std::string& path) {
68 SetPath(path);
70
71 auto cacheIt = g_modelCache.find(path);
72 if (cacheIt != g_modelCache.end()) {
73 m_meshes = cacheIt->second.meshes;
74 textureFileNames = cacheIt->second.textureFileNames;
76 return true;
77 }
78
79 const bool success = init();
81 return success;
82}
83
85{
86 m_meshes.clear();
87 textureFileNames.clear();
88
89 const std::string cachePath = GetBinaryCachePath();
90 if (IsBinaryCacheUpToDate(m_filePath, cachePath) && LoadBinaryCache(cachePath)) {
91 g_modelCache[m_filePath] = ModelCacheEntry{ m_meshes, textureFileNames };
92 return true;
93 }
94
95 const auto begin = std::chrono::high_resolution_clock::now();
96 std::vector<MeshComponent> loadedMeshes;
98 loadedMeshes = LoadFBXModel(m_filePath);
99 }
100 else if (m_modelType == ModelType::OBJ) {
101 loadedMeshes = LoadOBJModel(m_filePath);
102 }
103 const auto end = std::chrono::high_resolution_clock::now();
104 const auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
105
106 if (loadedMeshes.empty()) {
107 return false;
108 }
109
110 m_meshes = loadedMeshes;
111 g_modelCache[m_filePath] = ModelCacheEntry{ m_meshes, textureFileNames };
112 SaveBinaryCache(cachePath);
113
114 const std::wstring modelPathW(m_filePath.begin(), m_filePath.end());
115 MESSAGE("ModelLoader", "ModelLoader",
116 L"Loaded model '" << modelPathW << L"' in " << elapsedMs << L" ms. Meshes: " << m_meshes.size())
117 return true;
118}
119
121{
122 if (lScene) {
123 lScene->Destroy();
124 lScene = nullptr;
125 }
126 if (lSdkManager) {
127 lSdkManager->Destroy();
128 lSdkManager = nullptr;
129 }
130
132}
133
135{
136 size_t totalSize = 0;
137 for (const auto& mesh : m_meshes) {
138 totalSize += mesh.m_vertex.size() * sizeof(SimpleVertex);
139 totalSize += mesh.m_index.size() * sizeof(unsigned int);
140 }
141 return totalSize;
142}
143
144bool
146 lSdkManager = FbxManager::Create();
147 if (!lSdkManager) {
148 ERROR("ModelLoader", "FbxManager::Create()", "Unable to create FBX Manager!");
149 return false;
150 }
151
152 FbxIOSettings* ios = FbxIOSettings::Create(lSdkManager, IOSROOT);
153 lSdkManager->SetIOSettings(ios);
154
155 lScene = FbxScene::Create(lSdkManager, "MyScene");
156 if (!lScene) {
157 ERROR("ModelLoader", "FbxScene::Create()", "Unable to create FBX Scene!");
158 return false;
159 }
160 return true;
161}
162
163std::vector<MeshComponent>
164Model3D::LoadFBXModel(const std::string& filePath) {
165 std::vector<MeshComponent> loadedMeshes;
166
167 if (InitializeFBXManager()) {
168 FbxImporter* lImporter = FbxImporter::Create(lSdkManager, "");
169 if (!lImporter) {
170 ERROR("ModelLoader", "FbxImporter::Create()", "Unable to create FBX Importer!");
171 return loadedMeshes;
172 }
173
174 if (!lImporter->Initialize(filePath.c_str(), -1, lSdkManager->GetIOSettings())) {
175 ERROR("ModelLoader", "FbxImporter::Initialize()",
176 "Unable to initialize FBX Importer! Error: " << lImporter->GetStatus().GetErrorString());
177 lImporter->Destroy();
178 return loadedMeshes;
179 }
180
181 if (!lImporter->Import(lScene)) {
182 ERROR("ModelLoader", "FbxImporter::Import()",
183 "Unable to import FBX Scene! Error: " << lImporter->GetStatus().GetErrorString());
184 lImporter->Destroy();
185 return loadedMeshes;
186 }
187 else {
188 m_name = lImporter->GetFileName();
189 }
190
191 FbxAxisSystem::DirectX.ConvertScene(lScene);
192 FbxSystemUnit::m.ConvertScene(lScene);
193 FbxGeometryConverter gc(lSdkManager);
194 gc.Triangulate(lScene, true);
195
196 lImporter->Destroy();
197
198 FbxNode* lRootNode = lScene->GetRootNode();
199 if (lRootNode) {
200 m_meshes.clear();
201 for (int i = 0; i < lRootNode->GetChildCount(); i++) {
202 ProcessFBXNode(lRootNode->GetChild(i));
203 }
204 loadedMeshes = m_meshes;
205 return loadedMeshes;
206 }
207 else {
208 ERROR("ModelLoader", "FbxScene::GetRootNode()",
209 "Unable to get root node from FBX Scene!");
210 return loadedMeshes;
211 }
212 }
213 return loadedMeshes;
214}
215
216std::vector<MeshComponent>
217Model3D::LoadOBJModel(const std::string& filePath) {
218 struct ObjIndex {
219 int position = -1;
220 int texcoord = -1;
221 int normal = -1;
222
223 bool operator==(const ObjIndex& other) const {
224 return position == other.position &&
225 texcoord == other.texcoord &&
226 normal == other.normal;
227 }
228 };
229
230 struct ObjIndexHasher {
231 size_t operator()(const ObjIndex& index) const {
232 size_t seed = static_cast<size_t>(index.position + 1);
233 seed ^= static_cast<size_t>(index.texcoord + 1) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
234 seed ^= static_cast<size_t>(index.normal + 1) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
235 return seed;
236 }
237 };
238
239 struct ObjMeshBuilder {
240 std::string name = "default";
241 std::vector<SimpleVertex> vertices;
242 std::vector<unsigned int> indices;
243 std::unordered_map<ObjIndex, unsigned int, ObjIndexHasher> vertexLookup;
244 };
245
246 auto fixIndex = [](int index, int count) -> int {
247 if (index > 0) return index - 1;
248 if (index < 0) return count + index;
249 return -1;
250 };
251
252 auto normalize = [](EU::Vector3& value) {
253 const float lengthSq = value.x * value.x + value.y * value.y + value.z * value.z;
254 if (lengthSq <= 1e-20f) {
255 value = EU::Vector3(0.0f, 1.0f, 0.0f);
256 return;
257 }
258 const float invLength = 1.0f / std::sqrt(lengthSq);
259 value.x *= invLength;
260 value.y *= invLength;
261 value.z *= invLength;
262 };
263
264 auto parseFaceVertex = [&](const std::string& token,
265 int positionCount,
266 int texcoordCount,
267 int normalCount) -> ObjIndex {
268 ObjIndex result{};
269 size_t firstSlash = token.find('/');
270 size_t secondSlash = token.find('/', firstSlash == std::string::npos ? token.size() : firstSlash + 1);
271
272 const std::string positionToken = token.substr(0, firstSlash);
273 const std::string texcoordToken =
274 (firstSlash == std::string::npos) ? std::string() :
275 (secondSlash == std::string::npos ? token.substr(firstSlash + 1) : token.substr(firstSlash + 1, secondSlash - firstSlash - 1));
276 const std::string normalToken = (secondSlash == std::string::npos) ? std::string() : token.substr(secondSlash + 1);
277
278 if (!positionToken.empty()) result.position = fixIndex(std::stoi(positionToken), positionCount);
279 if (!texcoordToken.empty()) result.texcoord = fixIndex(std::stoi(texcoordToken), texcoordCount);
280 if (!normalToken.empty()) result.normal = fixIndex(std::stoi(normalToken), normalCount);
281
282 return result;
283 };
284
285 auto computeTangents = [&](MeshComponent& mesh) {
286 for (size_t i = 0; i + 2 < mesh.m_index.size(); i += 3) {
287 SimpleVertex& v0 = mesh.m_vertex[mesh.m_index[i + 0]];
288 SimpleVertex& v1 = mesh.m_vertex[mesh.m_index[i + 1]];
289 SimpleVertex& v2 = mesh.m_vertex[mesh.m_index[i + 2]];
290
291 const EU::Vector3 edge1 = v1.Position - v0.Position;
292 const EU::Vector3 edge2 = v2.Position - v0.Position;
293 const float du1 = v1.TextureCoordinate.x - v0.TextureCoordinate.x;
294 const float dv1 = v1.TextureCoordinate.y - v0.TextureCoordinate.y;
295 const float du2 = v2.TextureCoordinate.x - v0.TextureCoordinate.x;
296 const float dv2 = v2.TextureCoordinate.y - v0.TextureCoordinate.y;
297 const float denominator = du1 * dv2 - du2 * dv1;
298 const float invDenominator = std::fabs(denominator) < 1e-8f ? 0.0f : 1.0f / denominator;
299
300 const EU::Vector3 tangent(
301 (edge1.x * dv2 - edge2.x * dv1) * invDenominator,
302 (edge1.y * dv2 - edge2.y * dv1) * invDenominator,
303 (edge1.z * dv2 - edge2.z * dv1) * invDenominator);
304 const EU::Vector3 bitangent(
305 (edge2.x * du1 - edge1.x * du2) * invDenominator,
306 (edge2.y * du1 - edge1.y * du2) * invDenominator,
307 (edge2.z * du1 - edge1.z * du2) * invDenominator);
308
309 v0.Tangent += tangent;
310 v1.Tangent += tangent;
311 v2.Tangent += tangent;
312 v0.Bitangent += bitangent;
313 v1.Bitangent += bitangent;
314 v2.Bitangent += bitangent;
315 }
316
317 for (SimpleVertex& vertex : mesh.m_vertex) {
318 normalize(vertex.Normal);
319
320 const float tangentDotNormal =
321 vertex.Tangent.x * vertex.Normal.x +
322 vertex.Tangent.y * vertex.Normal.y +
323 vertex.Tangent.z * vertex.Normal.z;
324 vertex.Tangent = vertex.Tangent - (vertex.Normal * tangentDotNormal);
325 normalize(vertex.Tangent);
326
327 vertex.Bitangent = EU::Vector3(
328 vertex.Normal.y * vertex.Tangent.z - vertex.Normal.z * vertex.Tangent.y,
329 vertex.Normal.z * vertex.Tangent.x - vertex.Normal.x * vertex.Tangent.z,
330 vertex.Normal.x * vertex.Tangent.y - vertex.Normal.y * vertex.Tangent.x);
331 normalize(vertex.Bitangent);
332 }
333 };
334
335 auto flushMesh = [&](ObjMeshBuilder& builder, std::vector<MeshComponent>& meshes) {
336 if (builder.indices.empty() || builder.vertices.empty()) {
337 builder = ObjMeshBuilder{};
338 return;
339 }
340
341 MeshComponent mesh;
342 mesh.m_name = builder.name;
343 mesh.m_vertex = std::move(builder.vertices);
344 mesh.m_index = std::move(builder.indices);
345 mesh.m_numVertex = static_cast<int>(mesh.m_vertex.size());
346 mesh.m_numIndex = static_cast<int>(mesh.m_index.size());
347 computeTangents(mesh);
348 meshes.push_back(std::move(mesh));
349 builder = ObjMeshBuilder{};
350 };
351
352 std::ifstream file(filePath);
353 if (!file.is_open()) {
354 ERROR("ModelLoader", "LoadOBJModel", ("Unable to open OBJ file: " + filePath).c_str());
355 return {};
356 }
357
358 std::vector<EU::Vector3> positions;
359 std::vector<EU::Vector2> texcoords;
360 std::vector<EU::Vector3> normals;
361 std::vector<MeshComponent> loadedMeshes;
362 ObjMeshBuilder currentMesh;
363 std::string currentGroupName = "default";
364 currentMesh.name = currentGroupName;
365
366 std::string line;
367 while (std::getline(file, line)) {
368 if (line.empty() || line[0] == '#') {
369 continue;
370 }
371
372 std::istringstream stream(line);
373 std::string command;
374 stream >> command;
375
376 if (command == "v") {
377 EU::Vector3 position;
378 stream >> position.x >> position.y >> position.z;
379 positions.push_back(position);
380 }
381 else if (command == "vt") {
382 EU::Vector2 uv;
383 stream >> uv.x >> uv.y;
384 uv.y = 1.0f - uv.y;
385 texcoords.push_back(uv);
386 }
387 else if (command == "vn") {
388 EU::Vector3 normal;
389 stream >> normal.x >> normal.y >> normal.z;
390 normalize(normal);
391 normals.push_back(normal);
392 }
393 else if (command == "g" || command == "o") {
394 flushMesh(currentMesh, loadedMeshes);
395 stream >> currentGroupName;
396 if (currentGroupName.empty()) {
397 currentGroupName = "default";
398 }
399 currentMesh.name = currentGroupName;
400 }
401 else if (command == "usemtl") {
402 if (!currentMesh.indices.empty()) {
403 flushMesh(currentMesh, loadedMeshes);
404 }
405 currentMesh.name = currentGroupName;
406 }
407 else if (command == "f") {
408 std::vector<unsigned int> polygonIndices;
409 std::string token;
410 while (stream >> token) {
411 const ObjIndex objIndex = parseFaceVertex(
412 token,
413 static_cast<int>(positions.size()),
414 static_cast<int>(texcoords.size()),
415 static_cast<int>(normals.size()));
416
417 auto it = currentMesh.vertexLookup.find(objIndex);
418 if (it == currentMesh.vertexLookup.end()) {
419 SimpleVertex vertex{};
420 if (objIndex.position >= 0 && objIndex.position < static_cast<int>(positions.size())) {
421 vertex.Position = positions[objIndex.position];
422 }
423 if (objIndex.texcoord >= 0 && objIndex.texcoord < static_cast<int>(texcoords.size())) {
424 vertex.TextureCoordinate = texcoords[objIndex.texcoord];
425 }
426 else {
427 vertex.TextureCoordinate = EU::Vector2(0.0f, 0.0f);
428 }
429 if (objIndex.normal >= 0 && objIndex.normal < static_cast<int>(normals.size())) {
430 vertex.Normal = normals[objIndex.normal];
431 }
432 else {
433 vertex.Normal = EU::Vector3(0.0f, 1.0f, 0.0f);
434 }
435 vertex.Tangent = EU::Vector3(0.0f, 0.0f, 0.0f);
436 vertex.Bitangent = EU::Vector3(0.0f, 0.0f, 0.0f);
437
438 const unsigned int newIndex = static_cast<unsigned int>(currentMesh.vertices.size());
439 currentMesh.vertices.push_back(vertex);
440 currentMesh.vertexLookup[objIndex] = newIndex;
441 polygonIndices.push_back(newIndex);
442 }
443 else {
444 polygonIndices.push_back(it->second);
445 }
446 }
447
448 for (size_t i = 1; i + 1 < polygonIndices.size(); ++i) {
449 currentMesh.indices.push_back(polygonIndices[0]);
450 currentMesh.indices.push_back(polygonIndices[i]);
451 currentMesh.indices.push_back(polygonIndices[i + 1]);
452 }
453 }
454 }
455
456 flushMesh(currentMesh, loadedMeshes);
457 return loadedMeshes;
458}
459
460void
462 if (node->GetNodeAttribute()) {
463 if (node->GetNodeAttribute()->GetAttributeType() == FbxNodeAttribute::eMesh) {
464 ProcessFBXMesh(node);
465 }
466 }
467
468 for (int i = 0; i < node->GetChildCount(); i++) {
469 ProcessFBXNode(node->GetChild(i));
470 }
471}
472
473void
475 FbxMesh* mesh = node->GetMesh();
476 if (!mesh) return;
477
478 if (mesh->GetElementNormalCount() == 0)
479 mesh->GenerateNormals(true, true);
480
481 const char* uvSetName = nullptr;
482 {
483 FbxStringList uvSets; mesh->GetUVSetNames(uvSets);
484 if (uvSets.GetCount() > 0) uvSetName = uvSets[0];
485 }
486
487 if (mesh->GetElementTangentCount() == 0 && uvSetName)
488 mesh->GenerateTangentsData(uvSetName);
489
490 const FbxGeometryElementUV* uvElem = (mesh->GetElementUVCount() > 0) ? mesh->GetElementUV(0) : nullptr;
491 const FbxGeometryElementTangent* tanElem = (mesh->GetElementTangentCount() > 0) ? mesh->GetElementTangent(0) : nullptr;
492 const FbxGeometryElementBinormal* binElem = (mesh->GetElementBinormalCount() > 0) ? mesh->GetElementBinormal(0) : nullptr;
493
494 std::vector<SimpleVertex> vertices;
495 std::vector<unsigned int> indices;
496 vertices.reserve(mesh->GetPolygonCount() * 3);
497 indices.reserve(mesh->GetPolygonCount() * 3);
498
499 auto readV2 = [](const FbxGeometryElementUV* elem, int cpIdx, int pvIdx) -> FbxVector2 {
500 if (!elem) return FbxVector2(0, 0);
501 using E = FbxGeometryElement;
502 int idx;
503 if (elem->GetMappingMode() == E::eByControlPoint)
504 idx = (elem->GetReferenceMode() == E::eIndexToDirect) ? elem->GetIndexArray().GetAt(cpIdx) : cpIdx;
505 else
506 idx = (elem->GetReferenceMode() == E::eIndexToDirect) ? elem->GetIndexArray().GetAt(pvIdx) : pvIdx;
507 return elem->GetDirectArray().GetAt(idx);
508 };
509 auto readV4 = [](auto* elem, int cpIdx, int pvIdx) -> FbxVector4 {
510 if (!elem) return FbxVector4(0, 0, 0, 0);
511 using E = FbxGeometryElement;
512 int idx;
513 if (elem->GetMappingMode() == E::eByControlPoint)
514 idx = (elem->GetReferenceMode() == E::eIndexToDirect) ? elem->GetIndexArray().GetAt(cpIdx) : cpIdx;
515 else
516 idx = (elem->GetReferenceMode() == E::eIndexToDirect) ? elem->GetIndexArray().GetAt(pvIdx) : pvIdx;
517 return elem->GetDirectArray().GetAt(idx);
518 };
519
520 for (int p = 0; p < mesh->GetPolygonCount(); ++p)
521 {
522 const int polySize = mesh->GetPolygonSize(p);
523 std::vector<unsigned> cornerIdx; cornerIdx.reserve(polySize);
524
525 for (int v = 0; v < polySize; ++v)
526 {
527 const int cpIndex = mesh->GetPolygonVertex(p, v);
528 const int pvIndex = mesh->GetPolygonVertexIndex(p) + v;
529
530 SimpleVertex out{};
531
532 FbxVector4 P = mesh->GetControlPointAt(cpIndex);
533 out.Position = { (float)P[0], (float)P[1], (float)P[2] };
534
535 FbxVector4 N(0, 1, 0, 0);
536 mesh->GetPolygonVertexNormal(p, v, N);
537 N.Normalize();
538 out.Normal = { (float)N[0], (float)N[1], (float)N[2] };
539
540 if (uvElem && uvSetName) {
541 int uvIdx = mesh->GetTextureUVIndex(p, v);
542 FbxVector2 uv = (uvIdx >= 0) ? uvElem->GetDirectArray().GetAt(uvIdx)
543 : readV2(uvElem, cpIndex, pvIndex);
544 out.TextureCoordinate = { (float)uv[0], 1.0f - (float)uv[1] };
545 }
546 else {
547 out.TextureCoordinate = { 0.0f, 0.0f };
548 }
549
550 if (tanElem) {
551 FbxVector4 T = readV4(tanElem, cpIndex, pvIndex);
552 out.Tangent = { (float)T[0], (float)T[1], (float)T[2] };
553 }
554 else out.Tangent = { 0,0,0 };
555
556 if (binElem) {
557 FbxVector4 B = readV4(binElem, cpIndex, pvIndex);
558 out.Bitangent = { (float)B[0], (float)B[1], (float)B[2] };
559 }
560 else out.Bitangent = { 0,0,0 };
561
562 cornerIdx.push_back((unsigned)vertices.size());
563 vertices.push_back(out);
564 }
565
566 for (int k = 1; k + 1 < polySize; ++k) {
567 indices.push_back(cornerIdx[0]);
568 indices.push_back(cornerIdx[k + 1]);
569 indices.push_back(cornerIdx[k]);
570 }
571 }
572
573 if (mesh->GetElementTangentCount() == 0 || mesh->GetElementBinormalCount() == 0)
574 {
575 auto add = [](EU::Vector3 a, const EU::Vector3& b) { a.x += b.x; a.y += b.y; a.z += b.z; return a; };
576 auto sub = [](const EU::Vector3& a, const EU::Vector3& b) { return EU::Vector3(a.x - b.x, a.y - b.y, a.z - b.z); };
577 auto mul = [](const EU::Vector3& a, float s) { return EU::Vector3(a.x * s, a.y * s, a.z * s); };
578
579 for (size_t i = 0; i + 2 < indices.size(); i += 3)
580 {
581 SimpleVertex& v0 = vertices[indices[i + 0]];
582 SimpleVertex& v1 = vertices[indices[i + 1]];
583 SimpleVertex& v2 = vertices[indices[i + 2]];
584
585 EU::Vector3 e1 = sub(v1.Position, v0.Position);
586 EU::Vector3 e2 = sub(v2.Position, v0.Position);
587
588 float du1 = v1.TextureCoordinate.x - v0.TextureCoordinate.x;
589 float dv1 = v1.TextureCoordinate.y - v0.TextureCoordinate.y;
590 float du2 = v2.TextureCoordinate.x - v0.TextureCoordinate.x;
591 float dv2 = v2.TextureCoordinate.y - v0.TextureCoordinate.y;
592
593 float denom = du1 * dv2 - du2 * dv1;
594 float r = (std::fabs(denom) < 1e-8f) ? 0.0f : 1.0f / denom;
595
596 EU::Vector3 T = mul(EU::Vector3(e1.x * dv2 - e2.x * dv1, e1.y * dv2 - e2.y * dv1, e1.z * dv2 - e2.z * dv1), r);
597 EU::Vector3 B = mul(EU::Vector3(e2.x * du1 - e1.x * du2, e2.y * du1 - e1.y * du2, e2.z * du1 - e1.z * du2), r);
598
599 v0.Tangent = add(v0.Tangent, T);
600 v1.Tangent = add(v1.Tangent, T);
601 v2.Tangent = add(v2.Tangent, T);
602 v0.Bitangent = add(v0.Bitangent, B);
603 v1.Bitangent = add(v1.Bitangent, B);
604 v2.Bitangent = add(v2.Bitangent, B);
605 }
606 }
607
608 bool autoDetectMirror = true;
609 bool forceFlipWinding = true;
610
611 bool mirrored = true;
612 if (autoDetectMirror) {
613 FbxAMatrix geo;
614 geo.SetT(node->GetGeometricTranslation(FbxNode::eSourcePivot));
615 geo.SetR(node->GetGeometricRotation(FbxNode::eSourcePivot));
616 geo.SetS(node->GetGeometricScaling(FbxNode::eSourcePivot));
617 FbxAMatrix world = node->EvaluateGlobalTransform() * geo;
618
619 FbxVector4 S = world.GetS();
620 double detScale = S[0] * S[1] * S[2];
621 mirrored = (detScale < 0.0);
622 }
623
624 if (mirrored || forceFlipWinding) {
625 for (size_t i = 0; i + 2 < indices.size(); i += 3)
626 std::swap(indices[i + 1], indices[i + 2]);
627
628 for (auto& v : vertices) {
629 v.Normal = { v.Normal.x, v.Normal.y, v.Normal.z };
630 v.Tangent = { v.Tangent.x, v.Tangent.y, v.Tangent.z };
631 v.Bitangent = { v.Bitangent.x, v.Bitangent.y, v.Bitangent.z };
632 }
633 }
634
635 auto dot3 = [](const EU::Vector3& a, const EU::Vector3& b) { return a.x * b.x + a.y * b.y + a.z * b.z; };
636 auto norm3 = [](EU::Vector3& v) { float l = std::sqrt(EU::EMax(1e-20f, v.x * v.x + v.y * v.y + v.z * v.z)); v.x /= l; v.y /= l; v.z /= l; };
637 auto sub3 = [](const EU::Vector3& a, const EU::Vector3& b) { return EU::Vector3(a.x - b.x, a.y - b.y, a.z - b.z); };
638 auto cross3 = [](const EU::Vector3& a, const EU::Vector3& b) {
639 return EU::Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
640 };
641
642 for (auto& v : vertices)
643 {
644 norm3(v.Normal);
645 float dTN = dot3(v.Tangent, v.Normal);
646 v.Tangent = sub3(v.Tangent, EU::Vector3(v.Normal.x * dTN, v.Normal.y * dTN, v.Normal.z * dTN));
647 norm3(v.Tangent);
648
649 EU::Vector3 Bcalc = cross3(v.Normal, v.Tangent);
650 float hand = (dot3(Bcalc, v.Bitangent) < 0.0f) ? -1.0f : 1.0f;
651 v.Bitangent = { Bcalc.x * hand, Bcalc.y * hand, Bcalc.z * hand };
652 norm3(v.Bitangent);
653 }
654
655 MeshComponent mc;
656 mc.m_name = node->GetName();
657 mc.m_vertex = std::move(vertices);
658 mc.m_index = std::move(indices);
659 mc.m_numVertex = (int)mc.m_vertex.size();
660 mc.m_numIndex = (int)mc.m_index.size();
661 m_meshes.push_back(std::move(mc));
662}
663
664void Model3D::ProcessFBXMaterials(FbxSurfaceMaterial* material)
665{
666 if (material) {
667 FbxProperty prop = material->FindProperty(FbxSurfaceMaterial::sDiffuse);
668 if (prop.IsValid()) {
669 int textureCount = prop.GetSrcObjectCount<FbxTexture>();
670 for (int i = 0; i < textureCount; ++i) {
671 FbxTexture* texture = FbxCast<FbxTexture>(prop.GetSrcObject<FbxTexture>(i));
672 if (texture) {
673 textureFileNames.push_back(texture->GetName());
674 }
675 }
676 }
677 }
678}
679
680std::string
682 return m_filePath + ".wvmesh";
683}
684
685bool
686Model3D::IsBinaryCacheUpToDate(const std::string& sourcePath, const std::string& cachePath) const {
687 ULONGLONG sourceWriteTime = 0;
688 ULONGLONG cacheWriteTime = 0;
689
690 if (!GetFileWriteTime(sourcePath, sourceWriteTime)) {
691 return false;
692 }
693
694 if (!GetFileWriteTime(cachePath, cacheWriteTime)) {
695 return false;
696 }
697
698 return cacheWriteTime >= sourceWriteTime;
699}
700
701bool
702Model3D::LoadBinaryCache(const std::string& cachePath) {
703 std::ifstream stream(cachePath, std::ios::binary);
704 if (!stream.is_open()) {
705 return false;
706 }
707
708 uint32_t magic = 0;
709 uint32_t version = 0;
710 uint32_t meshCount = 0;
711 uint32_t textureCount = 0;
712
713 stream.read(reinterpret_cast<char*>(&magic), sizeof(magic));
714 stream.read(reinterpret_cast<char*>(&version), sizeof(version));
715 stream.read(reinterpret_cast<char*>(&meshCount), sizeof(meshCount));
716 stream.read(reinterpret_cast<char*>(&textureCount), sizeof(textureCount));
717
718 if (!stream.good() || magic != kModelCacheMagic || version != kModelCacheVersion) {
719 return false;
720 }
721
722 std::vector<MeshComponent> loadedMeshes;
723 std::vector<std::string> loadedTextures;
724 loadedMeshes.reserve(meshCount);
725 loadedTextures.reserve(textureCount);
726
727 for (uint32_t i = 0; i < textureCount; ++i) {
728 std::string textureName;
729 if (!ReadString(stream, textureName)) {
730 return false;
731 }
732 loadedTextures.push_back(std::move(textureName));
733 }
734
735 for (uint32_t i = 0; i < meshCount; ++i) {
736 MeshComponent mesh;
737 if (!ReadString(stream, mesh.m_name)) {
738 return false;
739 }
740
741 uint32_t vertexCount = 0;
742 uint32_t indexCount = 0;
743 stream.read(reinterpret_cast<char*>(&vertexCount), sizeof(vertexCount));
744 stream.read(reinterpret_cast<char*>(&indexCount), sizeof(indexCount));
745 if (!stream.good()) {
746 return false;
747 }
748
749 mesh.m_vertex.resize(vertexCount);
750 mesh.m_index.resize(indexCount);
751 if (vertexCount > 0) {
752 stream.read(reinterpret_cast<char*>(mesh.m_vertex.data()), sizeof(SimpleVertex) * vertexCount);
753 }
754 if (indexCount > 0) {
755 stream.read(reinterpret_cast<char*>(mesh.m_index.data()), sizeof(unsigned int) * indexCount);
756 }
757 if (!stream.good()) {
758 return false;
759 }
760
761 mesh.m_numVertex = static_cast<int>(vertexCount);
762 mesh.m_numIndex = static_cast<int>(indexCount);
763 loadedMeshes.push_back(std::move(mesh));
764 }
765
766 m_meshes = std::move(loadedMeshes);
767 textureFileNames = std::move(loadedTextures);
768
769 const std::wstring cachePathW(cachePath.begin(), cachePath.end());
770 MESSAGE("ModelLoader", "BinaryCache",
771 L"Loaded binary cache '" << cachePathW << L"'")
772 return true;
773}
774
775bool
776Model3D::SaveBinaryCache(const std::string& cachePath) const {
777 std::ofstream stream(cachePath, std::ios::binary | std::ios::trunc);
778 if (!stream.is_open()) {
779 return false;
780 }
781
782 const uint32_t meshCount = static_cast<uint32_t>(m_meshes.size());
783 const uint32_t textureCount = static_cast<uint32_t>(textureFileNames.size());
784
785 stream.write(reinterpret_cast<const char*>(&kModelCacheMagic), sizeof(kModelCacheMagic));
786 stream.write(reinterpret_cast<const char*>(&kModelCacheVersion), sizeof(kModelCacheVersion));
787 stream.write(reinterpret_cast<const char*>(&meshCount), sizeof(meshCount));
788 stream.write(reinterpret_cast<const char*>(&textureCount), sizeof(textureCount));
789
790 for (const std::string& textureName : textureFileNames) {
791 if (!WriteString(stream, textureName)) {
792 return false;
793 }
794 }
795
796 for (const MeshComponent& mesh : m_meshes) {
797 if (!WriteString(stream, mesh.m_name)) {
798 return false;
799 }
800
801 const uint32_t vertexCount = static_cast<uint32_t>(mesh.m_vertex.size());
802 const uint32_t indexCount = static_cast<uint32_t>(mesh.m_index.size());
803 stream.write(reinterpret_cast<const char*>(&vertexCount), sizeof(vertexCount));
804 stream.write(reinterpret_cast<const char*>(&indexCount), sizeof(indexCount));
805
806 if (vertexCount > 0) {
807 stream.write(reinterpret_cast<const char*>(mesh.m_vertex.data()), sizeof(SimpleVertex) * vertexCount);
808 }
809 if (indexCount > 0) {
810 stream.write(reinterpret_cast<const char*>(mesh.m_index.data()), sizeof(unsigned int) * indexCount);
811 }
812
813 if (!stream.good()) {
814 return false;
815 }
816 }
817
818 return stream.good();
819}
820
821
Declara la API de Model3D dentro del subsistema Core.
@ OBJ
Definition Model3D.h:14
@ FBX
Definition Model3D.h:15
#define MESSAGE(classObj, method, state)
#define ERROR(classObj, method, errorMSG)
A 2D vector class.
Definition Vector2.h:45
float y
The y-coordinate of the vector.
Definition Vector2.h:48
float x
The x-coordinate of the vector.
Definition Vector2.h:47
A 3D vector class.
Definition Vector3.h:45
float x
The x-coordinate of the vector.
Definition Vector3.h:47
float z
The z-coordinate of the vector.
Definition Vector3.h:49
float y
The y-coordinate of the vector.
Definition Vector3.h:48
void SetPath(const std::string &path)
Definition IResource.h:47
std::string m_name
Definition IResource.h:59
std::string m_filePath
Definition IResource.h:60
void SetState(ResourceState s)
Definition IResource.h:49
Componente ECS que almacena la información de geometría (malla) de un actor.
int m_numVertex
Número total de vértices en la malla.
std::vector< unsigned int > m_index
Lista de índices que definen las primitivas de la malla.
std::vector< SimpleVertex > m_vertex
Lista de vértices de la malla.
int m_numIndex
Número total de índices en la malla.
std::string m_name
Nombre de la malla.
std::vector< MeshComponent > LoadOBJModel(const std::string &filePath)
Definition Model3D.cpp:217
std::string GetBinaryCachePath() const
Definition Model3D.cpp:681
size_t getSizeInBytes() const override
Definition Model3D.cpp:134
std::vector< MeshComponent > LoadFBXModel(const std::string &filePath)
Definition Model3D.cpp:164
bool InitializeFBXManager()
Definition Model3D.cpp:145
void unload() override
Definition Model3D.cpp:120
std::vector< std::string > textureFileNames
Definition Model3D.h:85
bool IsBinaryCacheUpToDate(const std::string &sourcePath, const std::string &cachePath) const
Definition Model3D.cpp:686
void ProcessFBXNode(FbxNode *node)
Definition Model3D.cpp:461
FbxManager * lSdkManager
Definition Model3D.h:83
~Model3D() override
Definition Model3D.cpp:62
void ProcessFBXMaterials(FbxSurfaceMaterial *material)
Definition Model3D.cpp:664
bool LoadBinaryCache(const std::string &cachePath)
Definition Model3D.cpp:702
FbxScene * lScene
Definition Model3D.h:84
std::vector< MeshComponent > m_meshes
Definition Model3D.h:88
ModelType m_modelType
Definition Model3D.h:87
bool init() override
Definition Model3D.cpp:84
bool SaveBinaryCache(const std::string &cachePath) const
Definition Model3D.cpp:776
bool load(const std::string &path) override
Definition Model3D.cpp:67
void ProcessFBXMesh(FbxNode *node)
Definition Model3D.cpp:474
float EMax(float a, float b)
Devuelve el valor máximo de dos números.
Definition EngineMath.h:117
bool GetFileWriteTime(const std::string &path, ULONGLONG &outWriteTime)
Definition Model3D.cpp:25
std::unordered_map< std::string, ModelCacheEntry > g_modelCache
Definition Model3D.cpp:23
bool ReadString(std::ifstream &stream, std::string &value)
Definition Model3D.cpp:47
constexpr uint32_t kModelCacheVersion
Definition Model3D.cpp:16
constexpr uint32_t kModelCacheMagic
Definition Model3D.cpp:15
bool WriteString(std::ofstream &stream, const std::string &value)
Definition Model3D.cpp:38
EU::Vector2 TextureCoordinate
EU::Vector3 Bitangent
EU::Vector3 Tangent
EU::Vector3 Position
std::vector< std::string > textureFileNames
Definition Model3D.cpp:20