Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
DVDConverter.cpp
Go to the documentation of this file.
1
2
3#include "config.h"
4
7
17
18#include <assimp/DefaultLogger.hpp>
19#include <assimp/Importer.hpp>
20#include <assimp/Logger.hpp>
21#include <assimp/LogStream.hpp>
22#include <assimp/GltfMaterial.h>
23#include <assimp/postprocess.h>
24#include <assimp/scene.h>
25#include <assimp/types.h>
26
27#include <meshoptimizer.h>
28
29namespace Divide {
30
31namespace {
32 // Meshes with fewer than g_minIndexCountForAutoLoD indices will skip auto-LoD generation
33 constexpr size_t g_minIndexCountForAutoLoD = 4096;
34
35 struct AssimpStream final : Assimp::LogStream {
36 void write(const char* message) override
37 {
38 Console::printfn("{}", message);
39 }
40 };
41
42 // Select the kinds of messages you want to receive on this log stream
43 constexpr U32 g_severity = Config::Build::IS_DEBUG_BUILD ? Assimp::Logger::VERBOSE : Assimp::Logger::NORMAL;
44
45 constexpr bool g_removeLinesAndPoints = true;
46
48 {
49 U8 _boneID = 0;
50 F32 _boneWeight = 0.0f;
51 };
52
54 Bone* CreateBoneTree(aiNode* pNode, Bone* parent)
55 {
56 Bone* internalNode = new Bone(pNode->mName.data);
57 // set the parent; in case this is the root node, it will be null
58 internalNode->_parent = parent;
59 mat4<F32> out;
60 AnimUtils::TransformMatrix(pNode->mTransformation, out);
61 internalNode->localTransform(out);
62 internalNode->originalLocalTransform(internalNode->localTransform());
64
65 // continue for all child nodes and assign the created internal nodes as our
66 // children recursively call this function on all children
67 for (U32 i = 0u; i < pNode->mNumChildren; ++i)
68 {
69 internalNode->_children.push_back(CreateBoneTree(pNode->mChildren[i], internalNode));
70 }
71
72 return internalNode;
73 }
74}; //namespace
75
76namespace DVDConverter {
77namespace detail {
79 {
81 wrapMap[aiTextureMapMode_Wrap] = TextureWrap::REPEAT;
82 wrapMap[aiTextureMapMode_Clamp] = TextureWrap::CLAMP_TO_EDGE;
83 wrapMap[aiTextureMapMode_Mirror] = TextureWrap::MIRROR_REPEAT;
84 wrapMap[aiTextureMapMode_Decal] = TextureWrap::CLAMP_TO_EDGE; //With transparent border!
85 return wrapMap;
86 }
87
89 {
91 shadingMap[aiShadingMode_PBR_BRDF] = ShadingMode::PBR_MR;
92 shadingMap[aiShadingMode_Fresnel] = ShadingMode::BLINN_PHONG;
93 shadingMap[aiShadingMode_NoShading] = ShadingMode::FLAT;
94 //shadingMap[aiShadingMode_Unlit] = ShadingMode::FLAT; //Alias
95 shadingMap[aiShadingMode_CookTorrance] = ShadingMode::BLINN_PHONG;
96 shadingMap[aiShadingMode_Minnaert] = ShadingMode::BLINN_PHONG;
97 shadingMap[aiShadingMode_OrenNayar] = ShadingMode::BLINN_PHONG;
98 shadingMap[aiShadingMode_Toon] = ShadingMode::BLINN_PHONG;
99 shadingMap[aiShadingMode_Blinn] = ShadingMode::BLINN_PHONG;
100 shadingMap[aiShadingMode_Phong] = ShadingMode::BLINN_PHONG;
101 shadingMap[aiShadingMode_Gouraud] = ShadingMode::BLINN_PHONG;
102 shadingMap[aiShadingMode_Flat] = ShadingMode::FLAT;
103 return shadingMap;
104 }
105
107 {
109 operationMap[aiTextureOp_Multiply] = TextureOperation::MULTIPLY;
110 operationMap[aiTextureOp_Add] = TextureOperation::ADD;
111 operationMap[aiTextureOp_Subtract] = TextureOperation::SUBTRACT;
112 operationMap[aiTextureOp_Divide] = TextureOperation::DIVIDE;
113 operationMap[aiTextureOp_SmoothAdd] = TextureOperation::SMOOTH_ADD;
114 operationMap[aiTextureOp_SignedAdd] = TextureOperation::SIGNED_ADD;
115 operationMap[/*aiTextureOp_Replace*/ 7] = TextureOperation::REPLACE;
116 return operationMap;
117 }
118
122}; //namespace detail
123
124void OnStartup([[maybe_unused]] const PlatformContext& context)
125{
126 Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE);
127 Assimp::DefaultLogger::get()->attachStream(new AssimpStream(), g_severity);
128}
129
131{
132 Assimp::DefaultLogger::kill();
133}
134
135U32 PopulateNodeData(aiNode* node, MeshNodeData& target, const aiMatrix4x4& axisCorrectionBasis) {
136 if (node == nullptr) {
137 return 0u;
138 }
139
140 AnimUtils::TransformMatrix(axisCorrectionBasis * node->mTransformation, target._transform);
141 if (!COMPARE(target._transform.m[0][0], target._transform.m[1][1]) &&
142 !COMPARE(target._transform.m[1][1], target._transform.m[2][2]))
143 {
144 // We have either:
145 // - Non-uniform scale
146 // - Different transform axis (e.g. Z-up)
147 // Need to determine which is which.
148 aiVector3D pScaling, pPosition;
149 aiQuaternion pRotation;
150 (axisCorrectionBasis * node->mTransformation).Decompose(pScaling, pRotation, pPosition);
151 target._transform = mat4<F32>{
152 vec3<F32>(pPosition.x, pPosition.y, pPosition.y),
153 vec3<F32>(pScaling.x, pScaling.y, pScaling.y),
154 GetMatrix(Quaternion<F32>{ pRotation.x, pRotation.y, pRotation.z, pRotation.w })
155 };
156 }
157
158 target._name = node->mName.C_Str();
159 target._meshIndices.resize(node->mNumMeshes);
160 for (U32 i = 0u; i < node->mNumMeshes; ++i) {
161 target._meshIndices[i] = node->mMeshes[i];
162 }
163
164 U32 numChildren = 0u;
165 target._children.resize(node->mNumChildren);
166 for (U32 i = 0u; i < node->mNumChildren; ++i) {
167 numChildren += PopulateNodeData(node->mChildren[i], target._children[i], axisCorrectionBasis);
168 }
169
170 return numChildren + node->mNumChildren;
171}
172
174{
175 const ResourcePath& filePath = target.modelPath();
176 const Str<256>& fileName = target.modelName();
177
178 Assimp::Importer importer;
179
180 importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, g_removeLinesAndPoints ? aiPrimitiveType_LINE | aiPrimitiveType_POINT : 0);
181 //importer.SetPropertyInteger(AI_CONFIG_IMPORT_FBX_SEARCH_EMBEDDED_TEXTURES, 1);
182 importer.SetPropertyInteger(AI_CONFIG_PP_FD_REMOVE, 1);
183 importer.SetPropertyInteger(AI_CONFIG_IMPORT_TER_MAKE_UVS, 1);
184 importer.SetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 1);
185 importer.SetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, 80.0f);
186
187 constexpr auto ppSteps = aiProcess_GlobalScale |
188 aiProcess_CalcTangentSpace |
189 aiProcess_JoinIdenticalVertices |
190 aiProcess_ImproveCacheLocality |
191 aiProcess_GenSmoothNormals |
192 aiProcess_LimitBoneWeights |
193 aiProcess_RemoveRedundantMaterials |
194 //aiProcess_FixInfacingNormals | // Causes issues with backfaces inside the Sponza Atrium model
195 aiProcess_SplitLargeMeshes |
196 aiProcess_FindInstances |
197 aiProcess_Triangulate |
198 aiProcess_GenUVCoords |
199 aiProcess_SortByPType |
200 aiProcess_FindDegenerates |
201 aiProcess_FindInvalidData |
202 (Config::Build::IS_DEBUG_BUILD ? aiProcess_ValidateDataStructure : 0) |
203 aiProcess_OptimizeMeshes |
204 aiProcess_GenBoundingBoxes |
205 aiProcess_TransformUVCoords;// Preprocess UV transformations (scaling, translation ...)
206
207 const string modelPath = (filePath / fileName).string();
208 const aiScene* aiScenePointer = importer.ReadFile( modelPath.c_str(), to_U32(ppSteps) );
209
210 if (!aiScenePointer)
211 {
212 Console::errorfn(LOCALE_STR("ERROR_IMPORTER_FILE"), fileName, importer.GetErrorString());
213 return false;
214 }
215
216 aiMatrix4x4 axisCorrectionBasis;
217 if (aiScenePointer->mMetaData)
218 {
219 I32 UpAxis = 1, UpAxisSign = 1, FrontAxis = 2, FrontAxisSign = 1, CoordAxis = 0, CoordAxisSign = 1;
220 D64 UnitScaleFactor = 1.0;
221 aiScenePointer->mMetaData->Get<int>("UpAxis", UpAxis);
222 aiScenePointer->mMetaData->Get<int>("UpAxisSign", UpAxisSign);
223 aiScenePointer->mMetaData->Get<int>("FrontAxis", FrontAxis);
224 aiScenePointer->mMetaData->Get<int>("FrontAxisSign", FrontAxisSign);
225 aiScenePointer->mMetaData->Get<int>("CoordAxis", CoordAxis);
226 aiScenePointer->mMetaData->Get<int>("CoordAxisSign", CoordAxisSign);
227 aiScenePointer->mMetaData->Get<D64>("UnitScaleFactor", UnitScaleFactor);
228
229 aiVector3D upVec, forwardVec, rightVec;
230 upVec[to_U32(UpAxis)] = to_F32(UpAxisSign * UnitScaleFactor);
231 forwardVec[to_U32(FrontAxis)] = to_F32(FrontAxisSign * UnitScaleFactor);
232 rightVec[to_U32(CoordAxis)] = to_F32(CoordAxisSign * UnitScaleFactor);
233
234 axisCorrectionBasis = aiMatrix4x4(rightVec.x, rightVec.y, rightVec.z, 0.f,
235 upVec.x, upVec.y, upVec.z, 0.f,
236 forwardVec.x, forwardVec.y, forwardVec.z, 0.f,
237 0.f, 0.f, 0.f, 1.f);
238 }
239
240 const GeometryFormat format = GetGeometryFormatForExtension(getExtension(fileName).substr(1).c_str());
241
242 if (format == GeometryFormat::COUNT)
243 {
244 // unsupported
245 return false;
246 }
247
248 if ( aiScenePointer->HasAnimations() )
249 {
250 target._skeleton = CreateBoneTree(aiScenePointer->mRootNode, nullptr);
251
252 target._bones.reserve( target._skeleton->hierarchyDepth() );
253
254 mat4<F32> out;
255 U8 boneID = 0u;
256 for ( U16 meshPointer = 0u; meshPointer < aiScenePointer->mNumMeshes; ++meshPointer )
257 {
258 const aiMesh* mesh = aiScenePointer->mMeshes[meshPointer];
259 for ( U32 n = 0; n < mesh->mNumBones; ++n )
260 {
261 const aiBone* bone = mesh->mBones[n];
262
263 Bone* found = target._skeleton->find( bone->mName.data );
264 DIVIDE_ASSERT( found != nullptr, "DVDConverter::Load: Invalid skeleton detected while extracting bone data!" );
265
266 if ( found->boneID() == -1 )
267 {
268 AnimUtils::TransformMatrix( bone->mOffsetMatrix, out );
269 found->offsetMatrix( out );
270 found->boneID( boneID++ );
271 target._bones.push_back( found );
272 }
273 }
274 }
275
276 for (U32 i = 0u; i < aiScenePointer->mNumAnimations; ++i)
277 {
278 const aiAnimation* animation = aiScenePointer->mAnimations[i];
279 if (IS_ZERO(animation->mDuration))
280 {
281 Console::errorfn(LOCALE_STR("LOADED_0_LENGTH_ANIMATION"), animation->mName.C_Str());
282 }
283 else
284 {
285 target._animations.emplace_back(new AnimEvaluator(animation, i));
286 }
287 }
288 }
289
290 const U32 numMeshes = aiScenePointer->mNumMeshes;
291 target._subMeshData.reserve(numMeshes);
292
293 const U32 numChildren = PopulateNodeData(aiScenePointer->mRootNode, target._nodeData, axisCorrectionBasis);
294 DIVIDE_ASSERT(numChildren > 0u || !target._nodeData._meshIndices.empty(), "DVDConverter::Load: Error: Failed to find child nodes in specified file!");
295
296 constexpr U8 maxModelNameLength = 16;
297 constexpr U8 maxMeshNameLength = 64;
298
299 string modelName = stripExtension(fileName);
300 if (modelName.length() > maxModelNameLength)
301 {
302 modelName = modelName.substr(0, maxModelNameLength);
303 }
304
305 for (U16 n = 0u; n < numMeshes; ++n)
306 {
307 const aiMesh* currentMesh = aiScenePointer->mMeshes[n];
308 // Skip points and lines ... for now -Ionut
309 if (currentMesh->mNumVertices == 0)
310 {
311 continue;
312 }
313
314 const string subMeshName = currentMesh->mName.length == 0 ? Util::StringFormat("submesh_{}", n) : currentMesh->mName.C_Str();
315 const string fullName = Util::StringFormat("{}_{}_{}", subMeshName, n, modelName);
316
317 Import::SubMeshData subMeshTemp = {};
318 subMeshTemp.name(fullName.length() >= maxMeshNameLength ? fullName.substr(0, maxMeshNameLength - 1u).c_str() : fullName.c_str());
319 subMeshTemp.index(to_U32(n));
320 subMeshTemp.boneCount(to_U8(currentMesh->mNumBones));
321 detail::LoadSubMeshGeometry(currentMesh, subMeshTemp, target);
322
323 const ResourcePath& modelFolderName = getTopLevelFolderName(filePath);
324
326 aiScenePointer,
327 modelFolderName,
328 to_U16(currentMesh->mMaterialIndex),
329 Str<128>(subMeshTemp.name().c_str()) + "_material",
330 format,
331 true);
332
333
334 target._subMeshData.push_back(subMeshTemp);
335 }
336
337 detail::BuildGeometryBuffers(context, target);
338 return true;
339}
340
341namespace detail {
342
344{
345 size_t indexCount = 0u, vertexCount = 0u;
346 for (const Import::SubMeshData& data : target._subMeshData)
347 {
348 for ( U8 lod = 0u; lod < data.lodCount(); ++lod )
349 {
350 indexCount += data._indices[lod].size();
351 vertexCount += data._vertices[lod].size();
352 }
353 }
354
355 VertexBuffer::Descriptor descriptor{};
356 descriptor._allowDynamicUpdates = false;
357 descriptor._keepCPUData = true;
358 descriptor._largeIndices = vertexCount >= U16_MAX;
359 descriptor._name = target.modelName();
360
361 target._vertexBuffer = context.gfx().newVB( descriptor );
362 VertexBuffer* vb = target._vertexBuffer.get();
363
364 vb->setVertexCount(vertexCount);
365 vb->reserveIndexCount(indexCount);
366
367 U32 previousOffset = 0u;
368 for ( Import::SubMeshData& data : target._subMeshData )
369 {
371 const bool hasTexCoord = data._useAttribute[to_base( AttribLocation::TEXCOORD )];
372 const bool hasTangent = data._useAttribute[to_base( AttribLocation::TANGENT )];
373
374 U8 subMeshBoneOffset = 0u;
375 for ( U8 lod = 0u; lod < data.lodCount(); ++lod )
376 {
377 const size_t idxCount = data._indices[lod].size();
378
379 if (idxCount == 0u)
380 {
381 assert(lod > 0u);
382 subMeshBoneOffset += data.boneCount();
383 data._partitionIDs[lod] = data._partitionIDs[lod - 1];
384 continue;
385 }
386
387 data._triangles[lod].reserve(idxCount / 3);
388 const auto& indices = data._indices[lod];
389 for (size_t i = 0; i < idxCount; i += 3)
390 {
391 const U32 triangleTemp[3] =
392 {
393 indices[i + 0] + previousOffset,
394 indices[i + 1] + previousOffset,
395 indices[i + 2] + previousOffset
396 };
397
398 data._triangles[lod].emplace_back(triangleTemp[0], triangleTemp[1], triangleTemp[2]);
399
400 vb->addIndex(triangleTemp[0]);
401 vb->addIndex(triangleTemp[1]);
402 vb->addIndex(triangleTemp[2]);
403 }
404
405 auto& vertices = data._vertices[lod];
406 const U32 vertCount = to_U32(vertices.size());
407
408 for (U32 i = 0; i < vertCount; ++i)
409 {
410 const U32 targetIdx = i + previousOffset;
411
412 const Import::SubMeshData::Vertex& vert = vertices[i];
413
414 vb->modifyPositionValue(targetIdx, vert.position);
415 vb->modifyNormalValue(targetIdx, vert.normal);
416
417 if (hasTexCoord)
418 {
419 vb->modifyTexCoordValue(targetIdx, vert.texcoord.xy);
420 }
421 if (hasTangent)
422 {
423 vb->modifyTangentValue(targetIdx, vert.tangent.xyz);
424 }
425 }//vertCount
426
427 if (hasBones)
428 {
429 for (U32 i = 0; i < vertCount; ++i)
430 {
431 const U32 targetIdx = i + previousOffset;
432
433 Import::SubMeshData::Vertex& vert = vertices[i];
434 vec4<U8>& boneIndices = vert.indices;
435 for (U8& idx : boneIndices._v)
436 {
437 idx += subMeshBoneOffset;
438 }
439
440 vb->modifyBoneIndices(targetIdx, boneIndices);
441 vb->modifyBoneWeights(targetIdx, vert.weights);
442 }
443 }
444
445 subMeshBoneOffset += data.boneCount();
446 previousOffset += to_U32(vertices.size());
447 data._partitionIDs[lod] = vb->partitionBuffer();
448 } //submesh data
449 } //lod
450}
451
452
453void LoadSubMeshGeometry(const aiMesh* source, Import::SubMeshData& subMeshData, Import::ImportData& target)
454{
455 const bool isAnimated = target._animations.size();
456
457 subMeshData.maxPos( { source->mAABB.mMax.x, source->mAABB.mMax.y, source->mAABB.mMax.z } );
458 subMeshData.minPos( { source->mAABB.mMin.x, source->mAABB.mMin.y, source->mAABB.mMin.z } );
459 subMeshData.worldOffset( isAnimated ? VECTOR3_ZERO : ((subMeshData.maxPos() + subMeshData.minPos()) * 0.5f) );
460 subMeshData.maxPos( subMeshData.maxPos() - subMeshData.worldOffset() );
461 subMeshData.minPos( subMeshData.minPos() - subMeshData.worldOffset() );
462
463 vector<U32> input_indices(source->mNumFaces * 3u);
464 for (U32 j = 0u, k = 0u; k < source->mNumFaces; ++k)
465 {
466 const U32* indices = source->mFaces[k].mIndices;
467 // guaranteed to be 3 thanks to aiProcess_Triangulate
468 input_indices[j++] = indices[0];
469 input_indices[j++] = indices[1];
470 input_indices[j++] = indices[2];
471 }
472
473 vector<Import::SubMeshData::Vertex> vertices(source->mNumVertices);
474 for (U32 j = 0u; j < source->mNumVertices; ++j)
475 {
476 const aiVector3D position = source->mVertices[j];
477 const aiVector3D normal = source->mNormals[j];
478
479 vertices[j].position.set(vec3<F32>{ position.x, position.y, position.z } - subMeshData.worldOffset());
480 vertices[j].normal.set( normal.x, normal.y, normal.z );
481 }
482 subMeshData._useAttribute[to_base( AttribLocation::POSITION )] = true;
483 subMeshData._useAttribute[to_base( AttribLocation::NORMAL )] = true;
484
485 if (source->mTextureCoords[0] != nullptr)
486 {
487 for (U32 j = 0u; j < source->mNumVertices; ++j)
488 {
489 const aiVector3D texCoord = source->mTextureCoords[0][j];
490 vertices[j].texcoord.set(texCoord.x, texCoord.y, texCoord.z);
491 }
492 subMeshData._useAttribute[to_base( AttribLocation::TEXCOORD )] = true;
493 }
494
495 if (source->mTangents != nullptr)
496 {
497 for (U32 j = 0u; j < source->mNumVertices; ++j)
498 {
499 const aiVector3D tangent = source->mTangents[j];
500 vertices[j].tangent.set( tangent.x, tangent.y, tangent.z, 1.f);
501 }
502 subMeshData._useAttribute[to_base( AttribLocation::TANGENT )] = true;
503 }
504 else
505 {
506 Console::d_printfn(LOCALE_STR("SUBMESH_NO_TANGENT"), subMeshData.name().c_str());
507 }
508
509 if (source->mNumBones > 0u)
510 {
511 if (source->mNumBones >= Config::MAX_BONE_COUNT_PER_NODE )
512 {
513 Console::errorfn( LOCALE_STR( "SUBMESH_TOO_MANY_BONES" ), subMeshData.name().c_str(), source->mNumBones, Config::MAX_BONE_COUNT_PER_NODE , Config::MAX_BONE_COUNT_PER_NODE );
514 }
515
516 for ( U32 boneIndex = 0u; boneIndex < source->mNumBones; ++boneIndex )
517 {
518
519 Bone* bone = target._skeleton->find(source->mBones[boneIndex]->mName.data);
520 assert( bone->boneID() != -1);
521
522 aiVertexWeight* weights = source->mBones[boneIndex]->mWeights;
523 U32 numWeights = source->mBones[boneIndex]->mNumWeights;
524 for ( U32 weightIndex = 0; weightIndex < numWeights; ++weightIndex )
525 {
526 U32 vertexId = weights[weightIndex].mVertexId;
527 F32 weight = weights[weightIndex].mWeight;
528 assert( vertexId <= vertices.size() );
529
530 Import::SubMeshData::Vertex& vertex = vertices[vertexId];
531 for ( U8 i = 0; i < 4u; ++i )
532 {
533 if ( vertex.indices[i] == 0u )
534 {
535 vertex.weights[i] = weight;
536 vertex.indices[i] = to_U8( bone->boneID() );
537 break;
538 }
539 }
540 }
541 }
544 }
545
546 constexpr F32 kThreshold = 1.02f; // allow up to 2% worse ACMR to get more reordering opportunities for overdraw
547 constexpr D64 target_factor = 0.75; // index count reduction factor per LoD
548 constexpr F32 target_error = 1e-3f; // max allowed error between lod levels
549
550 { // Generate LoD 0 (max detail)
551 auto& target_indices = subMeshData._indices[0];
552 auto& target_vertices = subMeshData._vertices[0];
553
554 //Remap VB & IB
555 vector<U32> remap(source->mNumVertices);
556 const size_t vertex_count = meshopt_generateVertexRemap( remap.data(),
557 input_indices.data(),
558 input_indices.size(),
559 vertices.data(),
560 source->mNumVertices,
561 sizeof( Import::SubMeshData::Vertex ) );
562 const size_t index_count = input_indices.size();
563 target_indices.resize( index_count );
564 meshopt_remapIndexBuffer(target_indices.data(),
565 input_indices.data(),
566 input_indices.size(),
567 remap.data() );
568
569 // Don't need these anymore. Each LoD level will be calculated from the previous level's data
570 input_indices.clear();
571
572 target_vertices.resize( vertex_count );
573 meshopt_remapVertexBuffer( target_vertices.data(),
574 vertices.data(),
575 source->mNumVertices,
577 remap.data() );
578 vertices.clear();
579
580 // Optimise VB & IB
581 meshopt_optimizeVertexCache( target_indices.data(),
582 target_indices.data(),
583 index_count,
584 target_vertices.size() );
585
586 meshopt_optimizeOverdraw( target_indices.data(),
587 target_indices.data(),
588 index_count,
589 &target_vertices[0].position.x,
590 target_vertices.size(),
592 kThreshold );
593
594 // vertex fetch optimization should go last as it depends on the final index order
595 const size_t next_vertices = meshopt_optimizeVertexFetch( target_vertices.data(),
596 target_indices.data(),
597 index_count,
598 target_vertices.data(),
599 target_vertices.size(),
600 sizeof( Import::SubMeshData::Vertex ) );
601 target_vertices.resize( next_vertices );
602
603 subMeshData.lodCount( 1u );
604 }
605 { // Generate LoDs [1 ... Import::MAX_LOD_LEVELS-1] data and place inside VB & IB with proper offsets
606 if ( subMeshData._indices[0].size() >= g_minIndexCountForAutoLoD)
607 {
608 for (U8 i = 1u; i < Import::MAX_LOD_LEVELS; ++i)
609 {
610 auto& target_indices = subMeshData._indices[i];
611 auto& target_vertices = subMeshData._vertices[i];
612 auto& source_indices = subMeshData._indices[i - 1];
613 auto& source_vertices = subMeshData._vertices[i - i];
614
615 const size_t target_index_count = size_t(static_cast<D64>(source_indices.size()) * target_factor );
616
617 target_indices.resize( source_indices.size() );
618
619 const size_t next_indices = meshopt_simplify( target_indices.data(),
620 source_indices.data(),
621 source_indices.size(),
622 &source_vertices[0].position.x,
623 source_vertices.size(),
625 target_index_count,
626 target_error );
627 if (next_indices == source_indices.size() )
628 {
629 // We failed to simplify further, so no point in adding this as an LoD level
630 break;
631 }
632 target_indices.resize( next_indices );
633
634 // reorder indices for overdraw, balancing overdraw and vertex cache efficiency
635 meshopt_optimizeVertexCache(target_indices.data(),
636 target_indices.data(),
637 target_indices.size(),
638 source_vertices.size() );
639
640 meshopt_optimizeOverdraw( target_indices.data(),
641 target_indices.data(),
642 target_indices.size(),
643 &source_vertices[0].position.x,
644 source_vertices.size(),
646 kThreshold );
647
648 target_vertices.resize( source_vertices.size() );
649 const size_t next_vertices = meshopt_optimizeVertexFetch( target_vertices.data(),
650 target_indices.data(),
651 target_indices.size(),
652 source_vertices.data(),
653 source_vertices.size(),
654 sizeof( Import::SubMeshData::Vertex ) );
655 target_vertices.resize( next_vertices );
656
657 subMeshData.lodCount(subMeshData.lodCount() + 1u);
658 }
659 }
660 }
661}
662
664 const aiScene* source,
665 const ResourcePath& modelDirectoryName,
666 const U16 materialIndex,
667 const Str<128>& materialName,
668 const GeometryFormat format,
669 bool convertHeightToBumpMap)
670{
671
672 const aiMaterial* mat = source->mMaterials[materialIndex];
673
674 // ------------------------------- Part 1: Material properties --------------------------------------------
675 { // Load shading mode
676 // The default shading model should be set to the classic SpecGloss Phong
677 aiString matName;
678 if (AI_SUCCESS == mat->Get(AI_MATKEY_NAME, matName)) {
679 material.name(matName.C_Str());
680 } else {
681 material.name(materialName);
682 }
683
684 I32 shadingModel = 0, flags = 0;
685 if (AI_SUCCESS == aiGetMaterialInteger(mat, AI_MATKEY_SHADING_MODEL, &shadingModel)) {
686 material.shadingMode(detail::aiShadingModeInternalTable[to_U32(shadingModel)]);
687 } else {
688 material.shadingMode(ShadingMode::BLINN_PHONG);
689 }
690 if (material.shadingMode() == ShadingMode::PBR_MR) {
691 //From Assimp:
703 F32 temp = 0.f;
704 if (AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_GLOSSINESS_FACTOR, &temp)) {
705 material.shadingMode(ShadingMode::PBR_SG);
706 } else {
707 DIVIDE_ASSERT(AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_METALLIC_FACTOR, &temp));
708 }
709 }
710 aiGetMaterialInteger(mat, AI_MATKEY_TEXFLAGS_DIFFUSE(0), &flags);
711 const bool hasIgnoreAlphaFlag = (flags & aiTextureFlags_IgnoreAlpha) != 0;
712 if (hasIgnoreAlphaFlag) {
713 material.ignoreTexDiffuseAlpha(true);
714 }
715 const bool hasUseAlphaFlag = (flags & aiTextureFlags_UseAlpha) != 0;
716 if (hasUseAlphaFlag) {
717 material.ignoreTexDiffuseAlpha(false);
718 }
719 }
720
721 {// Load diffuse colour
722 material.baseColour(FColour4(0.8f, 0.8f, 0.8f, 1.f));
723 aiColor4D diffuse;
724 if (AI_SUCCESS == aiGetMaterialColor(mat, AI_MATKEY_COLOR_DIFFUSE, &diffuse)) {
725 material.baseColour(FColour4(diffuse.r, diffuse.g, diffuse.b, diffuse.a));
726 } else {
727 Console::d_printfn(LOCALE_STR("MATERIAL_NO_DIFFUSE"), materialName.c_str());
728 }
729 // Load material opacity value. Shouldn't really be used since we can use opacity maps for this
730 F32 alpha = 1.0f;
731 bool set = false;
732
733 if (AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_OPACITY, &alpha)) {
734 set = true;
735 } else if (AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_TRANSPARENCYFACTOR, &alpha)) {
736 alpha = 1.f - alpha;
737 set = true;
738 }
739
740 if (set && alpha > 0.f && alpha < 1.f) {
741 FColour4 base = material.baseColour();
742 base.a *= alpha;
743 material.baseColour(base);
744 }
745 }
746
747 { // Load specular colour
748 F32 specShininess = 0.f;
749 if (AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_SHININESS, &specShininess)) {
750 // Adjust shininess range here so that it always maps to the range [0,1000]
751 if (format == GeometryFormat::_3DS ||
752 format == GeometryFormat::ASE ||
753 format == GeometryFormat::FBX )
754 {
755 specShininess *= 10.f; // percentage (0-100%)
756 }
757 else if (format == GeometryFormat::OBJ)
758 {
759 NOP(); // 0...1000.f
760 }
761 else if (format == GeometryFormat::DAE)
762 {
763 REMAP(specShininess, 0.f, 511.f, 0.f, 1000.f); // 0.f ...511.f
764 }
765 else
766 {
767 specShininess = 1.f; // not supported. 0 = gouraud shading. If this ever changes (somehow) we need to handle it.
768 }
769
770 CLAMP(specShininess, 0.f, 1000.f);
771 }
772 // Once the value has been remaped to 0...1000, remap it what we can handle in the engine;
773 specShininess = MAP(specShininess, 0.f, 1000.f, 0.f, Material::MAX_SHININESS);
774
775 F32 specStrength = 1.f;
776 aiGetMaterialFloat(mat, AI_MATKEY_SHININESS_STRENGTH, &specStrength);
777
778 aiColor4D specular = {1.f, 1.f, 1.f, 1.f};
779 aiGetMaterialColor(mat, AI_MATKEY_COLOR_SPECULAR, &specular);
780
781 material.specular({ specular.r * specStrength, specular.g * specStrength, specular.b * specStrength, specShininess });
782 }
783 { // Load emissive colour
784 material.emissive(FColour3(0.f, 0.f, 0.f));
785 // Load emissive colour
786 aiColor4D emissive;
787 if (AI_SUCCESS == aiGetMaterialColor(mat, AI_MATKEY_COLOR_EMISSIVE, &emissive)) {
788 material.emissive(FColour3(emissive.r, emissive.g, emissive.b));
789 }
790 }
791 { // Load ambient colour
792 material.ambient(FColour3(0.f, 0.f, 0.f));
793 aiColor4D ambient;
794 if (AI_SUCCESS == aiGetMaterialColor(mat, AI_MATKEY_COLOR_AMBIENT, &ambient)) {
795 // Don't use this. Set it manually in the editor!
796 //material.ambient(FColour3(ambient.r, ambient.g, ambient.b));
797 }
798 }
799 { // Load metallic & roughness
800 material.metallic(0.f);
801 material.roughness(1.f);
802
803 F32 roughness = 0.f, metallic = 0.f;
804 // Load metallic
805 if (AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_METALLIC_FACTOR, &metallic)) {
806 material.metallic(metallic);
807 }
808 // Load roughness
809 if (AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_ROUGHNESS_FACTOR, &roughness)) {
810 material.roughness(roughness);
811 }
812 }
813 { // Load specular & glossiness
814 if (material.shadingMode() == ShadingMode::PBR_SG) {
815 FColour4 specularTemp;
816 specularTemp.rg = {1.f, 1.f};
817 aiGetMaterialFloat(mat, AI_MATKEY_SPECULAR_FACTOR, &specularTemp.r);
818 aiGetMaterialFloat(mat, AI_MATKEY_GLOSSINESS_FACTOR, &specularTemp.g);
819 material.specular(specularTemp);
820 }
821 }
822 { // Other material properties
823 F32 bumpScaling = 0.0f;
824 if (AI_SUCCESS == aiGetMaterialFloat(mat, AI_MATKEY_BUMPSCALING, &bumpScaling)) {
825 material.parallaxFactor(bumpScaling);
826 }
827
828 I32 twoSided = 0;
829 aiGetMaterialInteger(mat, AI_MATKEY_TWOSIDED, &twoSided);
830 material.doubleSided(twoSided != 0);
831 }
832
833 aiString tName;
834 aiTextureMapping mapping = aiTextureMapping_OTHER;
835 U32 uvInd = 0;
836 F32 blend = 0.0f;
837 aiTextureOp op = aiTextureOp_Multiply;
838 aiTextureMapMode mode[3] = { _aiTextureMapMode_Force32Bit,
839 _aiTextureMapMode_Force32Bit,
840 _aiTextureMapMode_Force32Bit };
841
842 const auto loadTexture = [&material, &modelDirectoryName](const TextureSlot usage, TextureOperation texOp, const aiString& name, aiTextureMapMode* wrapMode, const bool srgb = false)
843 {
844 DIVIDE_ASSERT(name.length > 0);
845 constexpr const char* g_backupImageExtensions[] =
846 {
847 "png", "jpg", "jpeg", "tga", "dds"
848 };
849
850 ResourcePath filePath = Paths::g_texturesLocation;
851 string fileName(name.C_Str());
852 const auto originalExtension = getExtension(fileName);
853
854 bool found = fileExists(filePath / fileName);
855 if (!found)
856 {
857 //Try backup extensions
858 string fileNameStem = stripExtension(fileName);
859 for (const char* ext : g_backupImageExtensions)
860 {
861 fileName = fileNameStem + "." + ext;
862
863 if (fileExists(filePath / fileName))
864 {
865 found = true;
866 break;
867 }
868 }
869 }
870 if (!found)
871 {
872 filePath = Paths::g_texturesLocation / modelDirectoryName;
873 fileName = stripExtension(fileName).append(originalExtension);
874
875 found = fileExists(filePath / fileName);
876 if (!found)
877 {
878 //Try backup extensions
879 string fileNameStem = stripExtension(fileName);
880 for (const char* ext : g_backupImageExtensions)
881 {
882 fileName = fileNameStem + "." + ext;
883 if (fileExists(filePath / fileName))
884 {
885 found = true;
886 break;
887 }
888 }
889 }
890 }
891
892 // if we have a name and an extension
893 if (found)
894 {
895 Import::TextureEntry& texture = material._textures[to_base(usage)];
896 // Load the texture resource
897 if (IS_IN_RANGE_INCLUSIVE(wrapMode[0], aiTextureMapMode_Wrap, aiTextureMapMode_Decal) &&
898 IS_IN_RANGE_INCLUSIVE(wrapMode[1], aiTextureMapMode_Wrap, aiTextureMapMode_Decal) &&
899 IS_IN_RANGE_INCLUSIVE(wrapMode[2], aiTextureMapMode_Wrap, aiTextureMapMode_Decal))
900 {
901 texture.wrapU(detail::aiTextureMapModeTable[wrapMode[0]]);
902 texture.wrapV(detail::aiTextureMapModeTable[wrapMode[1]]);
903 texture.wrapW(detail::aiTextureMapModeTable[wrapMode[2]]);
904 }
905
906 texture.textureName(fileName);
907 texture.texturePath(filePath);
908 texture.operation(texOp);
909 texture.srgb(srgb);
910 texture.useDDSCache(true);
911 texture.isNormalMap(usage == TextureSlot::NORMALMAP);
912 texture.alphaForTransparency(usage == TextureSlot::UNIT0 ||
913 usage == TextureSlot::UNIT1 ||
914 usage == TextureSlot::OPACITY);
915
916 material._textures[to_base(usage)] = texture;
917 }
918 };
919
920 // ------------------------------- Part 2: PBR or legacy material textures -------------------------------------
921 { // Albedo map
922 if (AI_SUCCESS == mat->GetTexture(aiTextureType_BASE_COLOR, 0, &tName, &mapping, &uvInd, &blend, &op, mode) ||
923 AI_SUCCESS == mat->GetTexture(aiTextureType_DIFFUSE, 0, &tName, &mapping, &uvInd, &blend, &op, mode))
924 {
925 if (tName.length > 0) {
926 // The first texture operation defines how we should mix the diffuse colour with the texture itself
927 loadTexture(TextureSlot::UNIT0, detail::aiTextureOperationTable[op], tName, mode, true);
928 } else {
929 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "0");
930 }
931 }
932 }
933 { // Detail map
934 if (AI_SUCCESS == mat->GetTexture(aiTextureType_BASE_COLOR, 1, &tName, &mapping, &uvInd, &blend, &op, mode) ||
935 AI_SUCCESS == mat->GetTexture(aiTextureType_DIFFUSE, 1, &tName, &mapping, &uvInd, &blend, &op, mode))
936 {
937 if (tName.length > 0) {
938 // The second operation is how we mix the albedo generated from the diffuse and Tex0 with this texture
939 loadTexture(TextureSlot::UNIT1, detail::aiTextureOperationTable[op], tName, mode, true);
940 } else {
941 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "UNIT1");
942 }
943 }
944 }
945 { // Validation
946 if (AI_SUCCESS == mat->GetTexture(aiTextureType_BASE_COLOR, 2, &tName, &mapping, &uvInd, &blend, &op, mode) ||
947 AI_SUCCESS == mat->GetTexture(aiTextureType_DIFFUSE, 2, &tName, &mapping, &uvInd, &blend, &op, mode)) {
948 Console::errorfn(LOCALE_STR("MATERIAL_EXTRA_DIFFUSE"), materialName.c_str());
949 }
950 }
951 bool hasNormalMap = false;
952 { // Normal map
953 if (AI_SUCCESS == mat->GetTexture(aiTextureType_NORMAL_CAMERA, 0, &tName, &mapping, &uvInd, &blend, &op, mode) ||
954 AI_SUCCESS == mat->GetTexture(aiTextureType_NORMALS, 0, &tName, &mapping, &uvInd, &blend, &op, mode))
955 {
956 if (tName.length > 0) {
957 loadTexture(TextureSlot::NORMALMAP, detail::aiTextureOperationTable[op], tName, mode);
958 material.bumpMethod(BumpMethod::NORMAL);
959 hasNormalMap = true;
960 } else {
961 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "NORMALMAP");
962 }
963 }
964 }
965 { // Height map or Displacement map. Just one here that acts as a parallax map. Height can act as a backup normalmap as well
966 if (AI_SUCCESS == mat->GetTexture(aiTextureType_HEIGHT, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
967 if (tName.length > 0) {
968 if (convertHeightToBumpMap && !hasNormalMap) {
969 loadTexture(TextureSlot::NORMALMAP, detail::aiTextureOperationTable[op], tName, mode);
970 material.bumpMethod(BumpMethod::NORMAL);
971 hasNormalMap = true;
972 } else {
973 loadTexture(TextureSlot::HEIGHTMAP, detail::aiTextureOperationTable[op], tName, mode);
974 material.bumpMethod(BumpMethod::PARALLAX);
975 }
976 } else {
977 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "NORMALMAP");
978 }
979 }
980
981 if (AI_SUCCESS == mat->GetTexture(aiTextureType_DISPLACEMENT, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
982 if (tName.length > 0) {
983 loadTexture(TextureSlot::HEIGHTMAP, detail::aiTextureOperationTable[op], tName, mode);
984 material.bumpMethod(BumpMethod::PARALLAX);
985 } else {
986 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "HEIGHTMAP");
987 }
988 }
989 }
990 { // Opacity map
991 if (AI_SUCCESS == mat->GetTexture(aiTextureType_OPACITY, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
992 if (tName.length > 0) {
993 loadTexture(TextureSlot::OPACITY, detail::aiTextureOperationTable[op], tName, mode);
994 } else {
995 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "OPACITY");
996 }
997 }
998 }
999 { // Specular map
1000 if (AI_SUCCESS == mat->GetTexture(aiTextureType_SPECULAR, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1001 if (tName.length > 0) {
1002 loadTexture(TextureSlot::SPECULAR, detail::aiTextureOperationTable[op], tName, mode);
1003 // Undo the spec colour and leave only the strength component in!
1004 material.specular({ 0.f, 0.f, 0.f, material.specular().a });
1005 } else {
1006 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "SPECULAR");
1007 }
1008 }
1009 }
1010 { // Emissive map
1011 if (AI_SUCCESS == mat->GetTexture(aiTextureType_EMISSIVE, 0, &tName, &mapping, &uvInd, &blend, &op, mode) ||
1012 AI_SUCCESS == mat->GetTexture(aiTextureType_EMISSION_COLOR, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1013 if (tName.length > 0) {
1014 loadTexture(TextureSlot::EMISSIVE, detail::aiTextureOperationTable[op], tName, mode);
1015 } else {
1016 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "EMISSIVE");
1017 }
1018 }
1019 }
1020 if (AI_SUCCESS == mat->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1021 if (tName.length > 0) {
1022 loadTexture(TextureSlot::METALNESS, detail::aiTextureOperationTable[op], tName, mode);
1023 } else {
1024 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "METALLIC_ROUGHNESS");
1025 }
1026 } else {
1027 if (AI_SUCCESS == mat->GetTexture(aiTextureType_METALNESS, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1028 if (tName.length > 0) {
1029 loadTexture(TextureSlot::METALNESS, detail::aiTextureOperationTable[op], tName, mode);
1030 } else {
1031 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "METALNESS");
1032 }
1033 }
1034 if (AI_SUCCESS == mat->GetTexture(aiTextureType_DIFFUSE_ROUGHNESS, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1035 if (tName.length > 0) {
1036 loadTexture(TextureSlot::ROUGHNESS, detail::aiTextureOperationTable[op], tName, mode);
1037 } else {
1038 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "ROUGHNESS");
1039 }
1040 }
1041 if (AI_SUCCESS == mat->GetTexture(aiTextureType_AMBIENT_OCCLUSION, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1042 if (tName.length > 0) {
1043 loadTexture(TextureSlot::OCCLUSION, detail::aiTextureOperationTable[op], tName, mode);
1044 } else {
1045 Console::errorfn(LOCALE_STR("MATERIAL_NO_NAME_TEXTURE"), materialName.c_str(), "OCCLUSION");
1046 }
1047 }
1048 }
1049 if (AI_SUCCESS == mat->GetTexture(aiTextureType_SHEEN, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1050 if (tName.length > 0) {
1051 Console::warnfn(LOCALE_STR("MATERIAL_TEXTURE_NOT_SUPPORTED"), materialName.c_str(), "SHEEN");
1052 }
1053 }
1054 if (AI_SUCCESS == mat->GetTexture(aiTextureType_CLEARCOAT, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1055 if (tName.length > 0) {
1056 Console::warnfn(LOCALE_STR("MATERIAL_TEXTURE_NOT_SUPPORTED"), materialName.c_str(), "CLEARCOAT");
1057 }
1058 }
1059 if (AI_SUCCESS == mat->GetTexture(aiTextureType_TRANSMISSION, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1060 if (tName.length > 0) {
1061 Console::warnfn(LOCALE_STR("MATERIAL_TEXTURE_NOT_SUPPORTED"), materialName.c_str(), "TRANSMISSION");
1062 }
1063 }
1064 if (AI_SUCCESS == mat->GetTexture(aiTextureType_UNKNOWN, 0, &tName, &mapping, &uvInd, &blend, &op, mode)) {
1065 if (tName.length > 0) {
1066 Console::warnfn(LOCALE_STR("MATERIAL_TEXTURE_NOT_SUPPORTED"), materialName.c_str(), "UNKNOWN");
1067 }
1068 }
1069}
1070}; //namespace detail
1071}; //namespace DVDConverter
1072}; //namespace Divide
#define LOCALE_STR(X)
Definition: Localization.h:91
#define DIVIDE_ASSERT(...)
#define NO_DESTROY
#define NOP()
Bone * _parent
Definition: Bone.h:51
size_t hierarchyDepth() const
Definition: Bone.h:68
Bone * find(const string &name)
Definition: Bone.h:77
vector< Bone * > _children
Definition: Bone.h:52
VertexBuffer_ptr newVB(const VertexBuffer::Descriptor &descriptor)
Create and return a new vertex array (VAO + VB + IB).
Definition: GFXDevice.cpp:3101
static constexpr F32 MAX_SHININESS
Definition: Material.h:129
GFXDevice & gfx() noexcept
void addIndex(const U32 index)
void modifyTexCoordValue(const U32 index, vec2< F32 > newValue)
void modifyBoneWeights(const U32 index, const FColour4 &weights)
void modifyTangentValue(const U32 index, const vec3< F32 > &newValue)
void reserveIndexCount(const size_t size)
void modifyNormalValue(const U32 index, const vec3< F32 > &newValue)
void setVertexCount(const size_t size)
void modifyBoneIndices(const U32 index, const vec4< U8 > indices)
void modifyPositionValue(const U32 index, const vec3< F32 > &newValue)
vec2< T > xy
Definition: MathVectors.h:852
vec3< T > xyz
Definition: MathVectors.h:1374
vec2< T > rg
Definition: MathVectors.h:1370
void TransformMatrix(const aiMatrix4x4 &in, mat4< F32 > &out, const bool rowMajor) noexcept
constexpr bool IS_DEBUG_BUILD
Definition: config.h:55
constexpr U16 MAX_BONE_COUNT_PER_NODE
Maximum number of bones available per node.
Definition: config.h:117
static hashMap< U32, ShadingMode > fillShadingModeMap()
void LoadSubMeshGeometry(const aiMesh *source, Import::SubMeshData &subMeshData, Import::ImportData &target)
static NO_DESTROY hashMap< U32, TextureOperation > aiTextureOperationTable
void BuildGeometryBuffers(PlatformContext &context, Import::ImportData &target)
static hashMap< U32, TextureOperation > fillTextureOperationMap()
static NO_DESTROY hashMap< U32, TextureWrap > aiTextureMapModeTable
static hashMap< U32, TextureWrap > fillTextureWrapMap()
void LoadSubMeshMaterial(Import::MaterialData &material, const aiScene *source, const ResourcePath &modelDirectoryName, const U16 materialIndex, const Str< 128 > &materialName, const GeometryFormat format, bool convertHeightToBumpMap)
static NO_DESTROY hashMap< U32, ShadingMode > aiShadingModeInternalTable
void OnStartup(const PlatformContext &context)
bool Load(PlatformContext &context, Import::ImportData &target)
U32 PopulateNodeData(aiNode *node, MeshNodeData &target, const aiMatrix4x4 &axisCorrectionBasis)
constexpr U8 MAX_LOD_LEVELS
Definition: MeshImporter.h:68
Str StringFormat(const char *fmt, Args &&...args)
Bone * CreateBoneTree(aiNode *pNode, Bone *parent)
Recursively creates an internal node structure matching the current scene and animation.
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
bool IS_ZERO(const T X) noexcept
constexpr U32 to_U32(const T value)
ResourcePath getTopLevelFolderName(const ResourcePath &filePath)
vec4< F32 > FColour4
Definition: MathHelper.h:73
bool IS_IN_RANGE_INCLUSIVE(const T x, const U min, const U max) noexcept
constexpr U16 to_U16(const T value)
int32_t I32
constexpr void REMAP(T &input, T in_min, T in_max, T out_min, T out_max, D64 &slopeOut) noexcept
Definition: MathHelper.inl:149
uint8_t U8
constexpr F32 to_F32(const T value)
GeometryFormat
Definition: MeshImporter.h:43
void CalculateBoneToWorldTransform(Bone *pInternalNode) noexcept
Calculates the global transformation matrix for the given internal node.
string getExtension(const std::string_view fileName)
GeometryFormat GetGeometryFormatForExtension(const char *extension) noexcept
TextureOperation
How should each texture be added.
eastl::vector< Type > vector
Definition: Vector.h:42
hashAlg::unordered_map< K, V, HashFun, Predicate > hashMap
Definition: HashMap.h:55
constexpr U16 U16_MAX
Project & parent
Definition: DefaultScene.h:41
uint16_t U16
::value constexpr T MAP(T input, T in_min, T in_max, T out_min, T out_max, D64 &slopeOut) noexcept
Definition: MathHelper.inl:141
constexpr U8 to_U8(const T value)
double D64
string stripExtension(const std::string_view fileName) noexcept
vec3< F32 > FColour3
Definition: MathHelper.h:70
mat3< T > GetMatrix(const Quaternion< T > &q) noexcept
Definition: Quaternion.inl:719
::value constexpr void CLAMP(T &n, T min, T max) noexcept
Clamps value n between min and max.
Definition: MathHelper.inl:114
bool COMPARE(T X, U Y) noexcept
bool fileExists(const ResourcePath &filePathAndName)
static const vec3< F32 > VECTOR3_ZERO
Definition: MathVectors.h:1434
uint32_t U32
TextureSlot
Definition: Material.h:76
constexpr auto to_base(const Type value) -> Type
static NO_INLINE void d_printfn(const char *format, T &&... args)
static NO_INLINE void errorfn(const char *format, T &&... args)
static NO_INLINE void warnfn(const char *format, T &&... args)
VertexBuffer_ptr _vertexBuffer
Definition: MeshImporter.h:159
vector< SubMeshData > _subMeshData
Definition: MeshImporter.h:167
Divide::MeshNodeData _nodeData
Definition: MeshImporter.h:166
vector< Bone * > _bones
Definition: MeshImporter.h:165
vector< AnimEvaluator * > _animations
Definition: MeshImporter.h:170
std::array< TextureEntry, to_base(TextureSlot::COUNT)> _textures
Definition: MeshImporter.h:109
vector< Vertex > _vertices[MAX_LOD_LEVELS]
Definition: MeshImporter.h:136
vector< U32 > _indices[MAX_LOD_LEVELS]
Definition: MeshImporter.h:135
vector< vec3< U32 > > _triangles[MAX_LOD_LEVELS]
Definition: MeshImporter.h:134
AttributeFlags _useAttribute
Definition: MeshImporter.h:138
std::array< U16, MAX_LOD_LEVELS > _partitionIDs
Definition: MeshImporter.h:133
Definition: MeshImporter.h:70
vector< MeshNodeData > _children
Definition: Mesh.h:70
mat4< F32 > _transform
Definition: Mesh.h:68
vector< U32 > _meshIndices
Index into Mesh::MeshData.
Definition: Mesh.h:69
string _name
Definition: Mesh.h:71