1#include "AssetImporterLoader.h"
5#include <assimp/Importer.hpp>
6#include <assimp/IOSystem.hpp>
7#include <assimp/IOStream.hpp>
8#include <assimp/postprocess.h>
9#include <assimp/mesh.h>
10#include <assimp/scene.h>
12#include "Resources/MeshManager.h"
13#include "Resources/ModelManager.h"
14#include "Resources/MaterialManager.h"
15#include "Resources/TextureManager.h"
16#include "Resources/DefaultMaterial.h"
17#include "Resources/AnimationManager.h"
18#include "Resources/ResourceStore.h"
19#include "Resources/Skeleton.h"
20#include "Resources/Animation.h"
22#include "Rendering/IGraphicsDevice.h"
23#include "Rendering/ITextures.h"
25#include "Foundation/Logging/Logger.h"
26#include "Foundation/Platform/IO.h"
27#include "Foundation/Platform/Timer.h"
29#include <glm/gtx/matrix_decompose.hpp>
35#include "Services/Variables.h"
44 glm::mat4 toGlm(aiMatrix4x4 matrix)
48 return glm::make_mat4((
float *)&(matrix));
51 glm::vec3 toGlm(aiVector3D v)
53 return glm::make_vec3((
float *)&(v));
56 glm::quat toGlm(aiQuaternion q)
58 return glm::quat(q.w, q.x, q.y, q.z);
61 class InputIOStream :
public Assimp::IOStream
65 resourceBuffer(resourceBuffer),
66 buffer(resourceBuffer.data()),
67 position(resourceBuffer.data()),
68 size(resourceBuffer.size())
73 size_t Read(
void * destBuffer,
size_t pSize,
size_t pCount)
override
75 const size_t bytesToRead = pSize * pCount;
76 const size_t offset = position - buffer;
79 auto bytesLeft = size - offset;
81 auto bytesRead = std::min(bytesToRead, bytesLeft);
84 ::memcpy(destBuffer, position, bytesRead);
85 position += bytesRead;
94 size_t Write(
const void * ,
size_t ,
size_t )
override
102#pragma warning(disable: 26812)
104 aiReturn Seek(
size_t pOffset, aiOrigin pOrigin)
override
108 position = buffer + pOffset;
114 position = buffer + size + pOffset;
120 return aiReturn_SUCCESS;
126 size_t Tell()
const override
128 return position - buffer;
131 size_t FileSize()
const override
136 void Flush()
override
143 const uint8_t * buffer;
144 const uint8_t * position;
148 class MmapIOStream :
public Assimp::IOStream
154 file = IO::openFile(path, FileHandle::OpenMode::OpenAlways, FileHandle::AccessMode::Read);
155 size = IO::fileSize(file);
156 buffer = (
const uint8_t *)IO::mapFile(file, FileHandle::ProtectionMode::ReadOnly, 0, size);
160 size_t Read(
void * destBuffer,
size_t pSize,
size_t pCount)
override
162 const size_t bytesToRead = pSize * pCount;
163 const size_t offset = position - buffer;
166 auto bytesLeft = size - offset;
168 auto bytesRead = std::min(bytesToRead, bytesLeft);
170 if (bytesRead != 0) {
171 ::memcpy(destBuffer, position, bytesRead);
172 position += bytesRead;
181 size_t Write(
const void * ,
size_t ,
size_t )
override
187 aiReturn Seek(
size_t pOffset, aiOrigin pOrigin)
override
191 position = buffer + pOffset;
197 position = buffer + size + pOffset;
203 return aiReturn_SUCCESS;
206 size_t Tell()
const override
208 return position - buffer;
211 size_t FileSize()
const override
216 void Flush()
override
223 Cogs::FileHandle::Ptr file;
224 const uint8_t * buffer =
nullptr;
225 const uint8_t * position =
nullptr;
229 class IOSystem :
public Assimp::IOSystem
232 IOSystem(
Context * context) : context(context)
234 useMmap = context->
variables->get(
"resources.useMemoryMappedIO",
false);
237 bool Exists(
const char * pFile)
const override
240 auto path = context->
resourceStore->getResourcePath(pFile, ResourceStoreFlags::NoCachedContent);
241 return IO::exists(path);
243 return context->
resourceStore->getResourceContents(pFile).size() != 0;
247 char getOsSeparator()
const override
249 return IO::pathSeparator();
252 Assimp::IOStream * Open(
const char* pFile,
const char* )
override
255 auto name = context->
resourceStore->getResourcePath(pFile, ResourceStoreFlags::NoCachedContent);
257 return new MmapIOStream(name.c_str());
259 auto content = context->
resourceStore->getResourceContents(pFile);
261 if (!content.size())
return nullptr;
263 return new InputIOStream(content);
267 void Close(Assimp::IOStream * pFile)
override
277 glm::vec3 xformPosition(
const glm::mat4 & transform,
const glm::vec3 & pos)
279 glm::vec4 c = transform * glm::vec4(pos.x, pos.y, pos.z, 1);
281 c = glm::vec4(0, 0, 0, 1.0f);
289 glm::vec3 xformVector(
const glm::mat4 & transform,
const glm::vec3 & n)
291 return xformPosition(transform, n) - xformPosition(transform, glm::vec3(0, 0, 0));
294 glm::vec3 calcNormal(
const glm::mat4 & transform,
const glm::vec3 & p1,
const glm::vec3 & p2,
const glm::vec3 & p3)
296 return glm::normalize(glm::cross(xformVector(transform, p2 - p1), xformVector(transform, p3 - p2)));
304 bool needInvertNormals(
const glm::mat4 & transform,
305 const std::vector<uint32_t> &indexes,
306 const glm::vec3 * points,
const glm::vec3 * normals,
307 const uint32_t count)
309 if (!indexes.empty()) {
310 for (
size_t p = 1; p < indexes.size() - 1; p += 3) {
311 const auto n = calcNormal(transform, points[indexes[p - 1]], points[indexes[p]], points[indexes[p + 1]]);
312 float dotProduct = glm::dot(n, glm::normalize(xformVector(transform, normals[indexes[p]])));
313 if (dotProduct > 0.5f)
315 else if (dotProduct < -0.5f)
319 for (uint32_t p = 1; p < count - 1; p += 3) {
320 const auto n = calcNormal(transform, points[p - 1], points[p], points[p + 1]);
321 float dotProduct = glm::dot(n, glm::normalize(xformVector(transform, normals[p])));
322 if (dotProduct > 0.5f)
324 else if (dotProduct < -0.5f)
334void Cogs::Core::AssetImporterLoader::handleBones(
const aiMesh * mesh,
Model & root,
const glm::mat4 & globalTransform,
Mesh * meshData)
336 const size_t numBones = mesh->mNumBones;
338 std::vector<int> numBoneWeights(mesh->mNumVertices);
339 std::vector<glm::ivec4> boneIndexes(mesh->mNumVertices);
340 std::vector<glm::vec4> boneWeights(mesh->mNumVertices);
342 bool weightsOutOfBounds =
false;
344 auto & skeleton = root.skeleton;
345 skeleton.bindPose = glm::inverse(globalTransform);
347 auto poseIndexes = meshData->mapPoseIndexes((uint32_t)numBones);
349 if (numBones > kMaxBones) {
350 LOG_WARNING(logger,
"Mesh bone count %zd exceeds max limit %zd.", numBones, kMaxBones);
353 for (
size_t i = 0; i < numBones; ++i) {
354 auto aBone = mesh->mBones[i];
356 std::string name = aBone->mName.C_Str();
358 auto fIt = skeleton.bonesByName.find(name);
359 if (fIt == skeleton.bonesByName.end())
continue;
361 auto boneIndex = fIt->second;
362 auto & bone = skeleton.bones[boneIndex];
364 poseIndexes[i] = (uint32_t)boneIndex;
368 if (!bone.hasOffset) {
369 bone.inverseBindPose = toGlm(aBone->mOffsetMatrix);
370 bone.hasOffset =
true;
373 auto numWeights = aBone->mNumWeights;
375 for (
size_t j = 0; j < numWeights; ++j) {
376 auto & weight = aBone->mWeights[j];
378 const int weightIndex = numBoneWeights[weight.mVertexId]++;
380 if (weightIndex > 3) {
381 if (!weightsOutOfBounds) {
382 LOG_WARNING(logger,
"Too many bone weights for vertex %d, extra weights will be dropped.", weight.mVertexId);
383 weightsOutOfBounds =
true;
388 glm::value_ptr(boneIndexes[weight.mVertexId])[weightIndex] =
static_cast<int>(i);
389 glm::value_ptr(boneWeights[weight.mVertexId])[weightIndex] = weight.mWeight;
395 meshData->
set(VertexDataType::Colors2, VertexFormats::BoneIndex4i, boneIndexes);
396 meshData->
set(VertexDataType::Colors3, VertexFormats::BoneWeight4f, boneWeights);
401void Cogs::Core::AssetImporterLoader::handleAssImpMesh(
Context * context,
Model & root,
ModelPart & modelPart,
const aiMesh * mesh,
const glm::mat4 & localTransform,
const glm::mat4 & globalTransform)
407 glm::vec3 translation;
409 glm::vec4 perspective;
410 glm::decompose(localTransform, scale, rotation, translation, skew, perspective);
411 const bool positiveScale = scale.x >= 0 && scale.y >= 0 && scale.z >= 0;
413 root.setPartTransform(modelPart, localTransform);
415 auto meshData = context->meshManager->createLocked();
417 modelPart.meshIndex = (uint32_t)root.meshes.size();
418 root.meshes.emplace_back(meshData.getHandle());
420 if (mesh->mName.length) {
421 meshData->
setName(mesh->mName.data);
424 std::vector<uint32_t> indexes;
426 if (mesh->mPrimitiveTypes == aiPrimitiveType_LINE || mesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE) {
427 size_t numIndices = 0;
428 for (uint32_t f = 0; f < mesh->mNumFaces; f++) {
429 const aiFace & face = mesh->mFaces[f];
430 numIndices += face.mNumIndices;
433 indexes.reserve(numIndices);
434 for (uint32_t f = 0; f < mesh->mNumFaces; f++) {
435 const aiFace & face = mesh->mFaces[f];
437 assert((mesh->mPrimitiveTypes == aiPrimitiveType_LINE) || face.mNumIndices == 3);
439 for (uint32_t i = 0; i < face.mNumIndices; i++) {
440 indexes.push_back(face.mIndices[i]);
444 LOG_WARNING(logger,
"Skipped geometry of type %x.", mesh->mPrimitiveTypes);
447 auto pPositions =
reinterpret_cast<const glm::vec3 *
>(mesh->mVertices);
448 meshData->
setPositions(pPositions, pPositions + mesh->mNumVertices);
450 if (mesh->mNormals !=
nullptr) {
451 auto pNormals =
reinterpret_cast<const glm::vec3 *
>(mesh->mNormals);
453 bool needInvert =
false;
454 if (!positiveScale && mesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE) {
455 needInvert = needInvertNormals(localTransform, indexes, pPositions, pNormals, mesh->mNumVertices);
462 meshData->
setNormals(pNormals, pNormals + mesh->mNumVertices);
467 if (mesh->mTangents) {
468 auto pTangents =
reinterpret_cast<glm::vec3 *
>(mesh->mTangents);
469 meshData->
setTangents(pTangents, pTangents + mesh->mNumVertices);
472 if (mesh->mTextureCoords[0] !=
nullptr) {
473 std::vector<glm::vec2> texCoords(mesh->mNumVertices);
475 for (
size_t i = 0; i < mesh->mNumVertices; ++i) {
476 texCoords[i] = glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
482 if (mesh->HasBones()) {
483 handleBones(mesh, root, globalTransform, meshData.operator->());
492 modelPart.boundsIndex =
static_cast<uint32_t
>(root.bounds.size());
493 root.bounds.push_back(bounds);
495 modelPart.materialIndex = mesh->mMaterialIndex;
498void Cogs::Core::AssetImporterLoader::handleAssImpNode(
Context * context,
Model & root,
size_t parent,
const aiScene * scene,
const aiNode * node,
const glm::mat4 & parentTransform)
500 const glm::mat4 localTransform = toGlm(node->mTransformation);
501 const glm::mat4 globalTransform = parentTransform * localTransform;
503 const size_t index = root.parts.size();
504 root.parts.emplace_back();
505 auto & modelNode = root.parts.back();
506 root.setPartTransform(modelNode, localTransform);
507 root.setPartName(modelNode, node->mName.C_Str());
508 modelNode.parentIndex = (uint32_t)parent;
510 if (node->mNumMeshes == 1) {
511 handleAssImpMesh(context, root, modelNode, scene->mMeshes[node->mMeshes[0]], localTransform, globalTransform);
513 for (uint32_t m = 0; m < node->mNumMeshes; m++) {
514 aiMesh * mesh = scene->mMeshes[node->mMeshes[m]];
516 root.parts.emplace_back();
517 auto & modelPart = root.parts.back();
518 modelPart.parentIndex = (uint32_t)index;
519 handleAssImpMesh(context, root, modelPart, mesh, localTransform, globalTransform);
523 for (uint32_t c = 0; c < node->mNumChildren; c++) {
524 aiNode * child = node->mChildren[c];
526 handleAssImpNode(context, root, index, scene, child, globalTransform);
533 case aiTextureMapMode_Wrap:
535 case aiTextureMapMode_Clamp:
537 case aiTextureMapMode_Decal:
539 case aiTextureMapMode_Mirror:
547 const aiScene * scene,
548 const aiMaterial * aiMaterial,
550 std::string texturePath,
551 aiTextureType textureType,
554 aiString textureresourceName;
555 aiTextureMapMode mode[2] = {};
556 if (aiMaterial->GetTexture(textureType, 0, &textureresourceName, 0, 0, 0, 0, mode) == aiReturn_SUCCESS) {
559 std::string textureFullPath = texturePath ==
"#" ?
561 Cogs::IO::fileName(textureresourceName.C_Str()) :
562 Cogs::IO::combine(texturePath, textureresourceName.C_Str());
564 std::string textureresourceNameStr(textureresourceName.C_Str());
565 std::replace(textureresourceNameStr.begin(), textureresourceNameStr.end(),
'\\',
'/');
566 std::string textureFullPath = texturePath ==
"#" ?
567 Cogs::IO::fileName(textureresourceNameStr) :
568 Cogs::IO::combine(texturePath, textureresourceNameStr);
571 auto textureFilename = IO::fileName(textureFullPath);
576 if (textureFilename.size() > 0 && textureFilename[0] ==
'*') {
577 unsigned int textureIndex;
579 if (sscanf_s(textureFilename.c_str(),
"*%u", &textureIndex) != 1) {
581 if (sscanf(textureFilename.c_str(),
"*%u", &textureIndex) != 1) {
583 LOG_WARNING(logger,
"Invalid embedded texture name '%s'.", textureFilename.c_str());
587 if (textureIndex >= scene->mNumTextures) {
588 LOG_WARNING(logger,
"Texture index to big: %u out of %u.", textureIndex, scene->mNumTextures);
592 auto textureData = scene->mTextures[textureIndex];
593 if (textureData->mHeight == 0) {
594 static size_t counter = 0;
595 std::string resourceName =
"Assimp_embedded_resource_" + std::to_string(counter++) +
"." + textureData->achFormatHint;
597 LOG_DEBUG(logger,
"Loading embedded, compressed texture. Name: '%s'", resourceName.c_str());
599 context->
resourceStore->addResource(resourceName, std::string(
reinterpret_cast<const char*
>(textureData->pcData), textureData->mWidth));
601 textureHandle = context->textureManager->loadTexture(resourceName, NoResourceId, flags);
603 LOG_DEBUG(logger,
"Loading embedded, un-compressed texture. Hint: '%s'", textureData->achFormatHint);
605 textureHandle = context->textureManager->loadTexture2D(textureData->pcData, textureData->mWidth, textureData->mHeight, Cogs::TextureFormat::R8G8B8A8_UNORM, 0, NoResourceId, flags);
608 textureHandle = context->textureManager->loadTexture(textureFullPath, NoResourceId, flags);
611 if (key != Cogs::Core::NoProperty) {
622void Cogs::Core::AssetImporterLoader::handleAssImpMaterial(
Context * context,
ModelLoadFlags flags,
const aiScene * scene,
const aiMaterial * aiMaterial,
MaterialInstance & material, std::string texturePath)
624 aiString materialName;
625 aiMaterial->Get(AI_MATKEY_NAME, materialName);
627 if (materialName.length) {
628 material.
setName(materialName.C_Str());
632 if (aiMaterial->Get(AI_MATKEY_OPACITY, opacity) != aiReturn_SUCCESS) {
637 if (aiMaterial->Get(AI_MATKEY_SHININESS, specularPower) == aiReturn_SUCCESS && specularPower != 0) {
642 if ((aiMaterial->Get(AI_MATKEY_TWOSIDED, twoSided) == aiReturn_SUCCESS && twoSided != 0) ||
648 if (aiMaterial->Get(AI_MATKEY_SHADING_MODEL, lightModel) == aiReturn_SUCCESS && lightModel == aiShadingMode_NoShading) {
649 material.setVariant(
"LightModel",
"BaseColor");
659 auto checkColor = [&](
const char * key,
int type,
int index,
const VariableKey & name,
float opacity,
bool is4Component =
false)
661 aiColor3D checkedColor;
662 if (aiMaterial->Get(key, type, index, checkedColor) == aiReturn_SUCCESS) {
665 material.
setVec4Property(name, glm::vec4(checkedColor.r, checkedColor.g, checkedColor.b, opacity));
667 material.
setVec3Property(name, glm::vec3(checkedColor.r, checkedColor.g, checkedColor.b));
672 checkColor(AI_MATKEY_COLOR_DIFFUSE, DefaultMaterial::DiffuseColor, opacity,
true);
673 checkColor(AI_MATKEY_COLOR_SPECULAR, DefaultMaterial::SpecularColor, 1);
674 checkColor(AI_MATKEY_COLOR_EMISSIVE, DefaultMaterial::EmissiveColor, 1);
676 bool hasDiffuse = loadTexture(context, scene, aiMaterial, material, texturePath, aiTextureType_DIFFUSE, DefaultMaterial::DiffuseMap);
677 bool hasSpecular = loadTexture(context, scene, aiMaterial, material, texturePath, aiTextureType_SPECULAR, DefaultMaterial::SpecularMap);
678 bool hasNormal = loadTexture(context, scene, aiMaterial, material, texturePath, aiTextureType_NORMALS, DefaultMaterial::NormalMap);
679 bool hasOpacity = loadTexture(context, scene, aiMaterial, material, texturePath, aiTextureType_OPACITY, DefaultMaterial::OpacityMap);
681 if (hasOpacity && hasDiffuse) {
683 material.setVariant(
"AlphaTest",
"Discard");
686 material.setPermutation(
"Default");
687 material.setVariant(
"EnableLighting",
"true");
689 if (hasSpecular || hasNormal || hasDiffuse) {
690 material.setVariant(
"Textured",
"true");
693 material.setVariant(
"TangentSpaceNormalMap",
"true");
697 material.setVariant(
"SpecularMap",
"true");
701 if (hasBones(scene, scene->mRootNode)) {
702 material.setVariant(
"Skinned",
true);
709#ifdef COGS_EXTENSIONS_TINYOBJLOADER
710 if (extension ==
".obj")
return false;
712 if (extension ==
".gltf")
return false;
713 if (extension ==
".glb")
return false;
714 if (extension ==
".rvm")
return false;
715 if (extension ==
".rvmdump")
return false;
719 Assimp::Importer importer;
720 return importer.IsExtensionSupported(extension);
723void addBones(
Skeleton & skeleton,
const aiNode * node,
size_t parentIndex)
725 auto localTransform = toGlm(node->mTransformation);
727 std::string name = node->mName.C_Str();
729 auto bone =
Bone{ name, parentIndex, glm::vec3(0.0), glm::vec3(1.0), glm::quat(), localTransform, glm::mat4(), glm::mat4(),
false,
false,
false };
731 skeleton.bones.emplace_back(bone);
733 const uint32_t index =
static_cast<uint32_t
>(skeleton.bones.size()) - 1;
735 skeleton.bonesByName[name] = index;
737 for (
size_t i = 0; i < node->mNumChildren; ++i) {
738 addBones(skeleton, node->mChildren[i], index);
742void buildSkeleton(
Model & model,
const aiNode * node)
744 auto & skeleton = model.skeleton;
746 addBones(skeleton, node, (
size_t)-1);
749void updateSkeleton(
Skeleton & skeleton)
751 if (skeleton.bones.empty())
return;
753 for (
auto & bone : skeleton.bones) {
754 bool isRoot = bone.parentBone == (size_t)-1;
757 bone.absolute = bone.relative;
759 auto & parent = skeleton.bones[bone.parentBone];
761 bone.absolute = parent.absolute * bone.relative;
766void handleAnimations(
Context * context,
Model & model,
const aiScene * scene)
768 model.animation = context->animationManager->create();
770 auto & animation = *model.animation.
resolve();
772 auto & skeleton = model.skeleton;
774 auto numAnims = scene->mNumAnimations;
776 for (
size_t i = 0; i < numAnims; ++i) {
777 auto & aAnim = scene->mAnimations[i];
779 animation.clips.emplace_back();
780 auto & clip = animation.clips.back();
782 clip.name = aAnim->mName.C_Str();
783 clip.duration =
static_cast<float>(aAnim->mDuration);
784 clip.resolution =
static_cast<float>(aAnim->mTicksPerSecond);
786 auto numChannels = aAnim->mNumChannels;
788 clip.tracks.resize(numChannels);
790 for (
size_t j = 0; j < numChannels; ++j) {
791 auto & aChannel = aAnim->mChannels[j];
792 std::string channelName = aChannel->mNodeName.C_Str();
794 auto & track = clip.tracks[j];
795 track.boneIndex = (uint32_t)-1;
797 if (skeleton.bonesByName.find(channelName) == model.skeleton.bonesByName.end()) {
800 auto boneIndex = model.skeleton.bonesByName[channelName];
802 track.boneIndex = boneIndex;
803 model.skeleton.bones[boneIndex].animated =
true;
805 for (
size_t k = 0; k < aChannel->mNumPositionKeys; ++k) {
806 const float time =
static_cast<float>(aChannel->mPositionKeys[k].mTime);
807 const glm::vec3 translation = toGlm(aChannel->mPositionKeys[k].mValue);
809 track.translations.emplace_back(
TranslationKey{ time, translation });
812 for (
size_t k = 0; k < aChannel->mNumRotationKeys; ++k) {
813 const float time =
static_cast<float>(aChannel->mRotationKeys[k].mTime);
814 const glm::quat rotation = toGlm(aChannel->mRotationKeys[k].mValue);
816 track.rotations.emplace_back(
RotationKey{ time, rotation });
819 for (
size_t k = 0; k < aChannel->mNumScalingKeys; ++k) {
820 const float time =
static_cast<float>(aChannel->mScalingKeys[k].mTime);
821 const glm::vec3 scale = toGlm(aChannel->mScalingKeys[k].mValue);
823 track.scales.emplace_back(
ScaleKey{ time, scale });
830inline bool isFolder(std::string path)
833 if (stat(path.c_str(), &sb) != 0)
return false;
835 auto dirMode = sb.st_mode & S_IFDIR;
839inline std::string toLower(std::string
const & str)
841 std::string ret(str);
842 for (
auto& c : ret) {
843 c =
static_cast<decltype(ret)::value_type
>(std::tolower(c));
848inline bool endsWith(std::string
const & value, std::string
const & ending)
850 if (ending.size() > value.size())
return false;
851 return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
856 auto timer = Timer::startNew();
858 LOG_DEBUG(logger,
"Loading model: %s", loadInfo.
resourceName.c_str());
860 auto & modelManager = *context->modelManager;
861 auto & materialManager = *context->materialManager;
862 auto & materialInstanceManager = *context->materialInstanceManager;
866 Assimp::Importer importer;
868 importer.SetIOHandler(
new IOSystem(context));
874 aiProcess_RemoveRedundantMaterials |
875 aiProcess_GenNormals |
876 aiProcess_FlipWindingOrder |
877 aiProcess_Triangulate;
881 flags |= aiProcess_FlipUVs;
885 flags |= aiProcess_ImproveCacheLocality |
886 aiProcess_SortByPType |
887 aiProcess_GenUVCoords |
888 aiProcess_OptimizeMeshes;
892 flags |= aiProcess_CalcTangentSpace;
895 const aiScene * scene =
nullptr;
896 bool hasMatLib =
false;
899 scene = importer.ReadFileFromMemory(
905 LOG_ERROR(logger,
"Data load size: %zu", loadInfo.
resourceData.size());
912 auto pos = path.find_last_of(
".");
913 auto base = path.substr(0, pos);
914 auto materialFolder = base +
".fbm";
915 if (isFolder(materialFolder)) {
916 context->
resourceStore->addSearchPath(materialFolder + IO::pathSeparator());
921 scene = importer.ReadFile(loadInfo.
resourcePath, flags);
925 scene = importer.ReadFileFromMemory(data.data(),
934 LOG_ERROR(logger,
"Error loading model: %s", importer.GetErrorString());
938 auto model = modelManager.lock(loadInfo.
handle);
940 bool needsTangents =
false;
943 const std::string texturePath = hasMatLib ?
"#" : IO::parentPath(loadInfo.
resourcePath);
945 for (uint32_t m = 0; m < scene->mNumMaterials; m++) {
946 const aiMaterial * aiMat = scene->mMaterials[m];
948 MaterialInstanceHandle materialHandle = materialInstanceManager.createMaterialInstance(materialManager.getDefaultMaterial());
951 handleAssImpMaterial(context, loadInfo.modelFlags, scene, aiMat, *material, texturePath);
954 if (material->getVariant(
"TangentSpaceNormalMap") ==
"true") {
955 needsTangents =
true;
958 model->materials.push_back(materialHandle);
961 if (needsTangents || (!tangents && optimize)) {
962 importer.ApplyPostProcessing(aiProcess_CalcTangentSpace);
965 if (hasBones(scene, scene->mRootNode)) {
966 buildSkeleton(*model, scene->mRootNode);
967 updateSkeleton(model->skeleton);
970 if (scene->HasAnimations()) {
971 handleAnimations(context, *model, scene);
974 glm::mat4 rootTransform(1.0f);
975 handleAssImpNode(context, *model, NoParentPart, scene, scene->mRootNode, rootTransform);
977 if (model->animation) {
978 model->animation->skeleton = model->skeleton;
981 LOG_TRACE(logger,
"Assimp elapsed: %f.", timer.elapsedSeconds());
987bool Cogs::Core::AssetImporterLoader::hasBones(
const aiScene * scene,
const aiNode * node)
989 for (uint32_t m = 0; m < node->mNumMeshes; m++) {
990 aiMesh * mesh = scene->mMeshes[node->mMeshes[m]];
991 if (mesh->HasBones()) {
996 for (uint32_t c = 0; c < node->mNumChildren; c++) {
997 aiNode * child = node->mChildren[c];
998 if (hasBones(scene, child))
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class Variables > variables
Variables service instance.
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
ModelLoadFlags
Model loading flags. May be combined with resource loading flags.
@ NeedsTangents
The importer should generate tangent data if not present.
@ NoFlipTexcoords
Do not flip texture coordinates.
@ LinearColorSpace
For textures with RGBA format without color space information, mark the data as being in linear color...
Cogs::Geometry::BoundingBox COGSCORE_DLL_API calculateBounds(Mesh *mesh)
Calculate a bounding box for the given mesh.
@ None
No primitive culling performed.
uint16_t VariableKey
Used to lookup material properties.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
@ TriangleList
List of triangles.
Material instances represent a specialized Material combined with state for all its buffers and prope...
void setFloatProperty(const VariableKey key, float value)
Set the float property with the given key to value.
void setTransparent()
Set the material instance to transparent, indicating to the renderer that blending should be enabled ...
void setVec3Property(const VariableKey key, glm::vec3 value)
Set the vec3 property with the given key to value.
void setTextureAddressMode(const StringView &key, const StringView &addressMode)
Set texture address mode with textual name.
void setTextureProperty(const StringView &key, TextureHandle value)
Set the texture property with the given key to the texture resource held by value.
MaterialOptions options
Material rendering options used by this instance.
void setVec4Property(const VariableKey key, glm::vec4 value)
Set the vec4 property with the given key to value.
TransparencyMode transparencyMode
Transparency mode.
CullMode cullMode
Primitive culling mode to use.
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
void setTangents(std::span< const glm::vec3 > tangents)
Set the tangent data of the Mesh.
void setIndexes(std::span< const uint32_t > collection)
Set the index data to the collection given.
void setTexCoords(std::span< const glm::vec2 > texCoords)
Set the texture coordinate data of the Mesh.
void setBounds(Geometry::BoundingBox box)
Set custom bounds for the mesh.
void set(const VertexDataType::EVertexDataType type, VertexFormatHandle format, Element *begin, Element *end)
Set the data of the vertex stream indexed by type.
void setNormals(std::span< const glm::vec3 > normals)
Set the normal data of the Mesh.
void setMeshFlag(MeshFlags::EMeshFlags flag)
Set the given mesh flag.
void setPositions(std::span< const glm::vec3 > positions)
Set the position data of the Mesh.
Model resources define a template for a set of connected entities, with resources such as meshes,...
void setName(const StringView &name)
Set the user friendly name of the resource.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
std::string resourcePath
Resource path. Used to locate resource.
std::string resourceName
Desired resource name. If no name is given, a default name will be chosen.
ResourceHandleBase handle
Handle to resource structure for holding actual resource data.
std::vector< uint8_t > resourceData
Resource load data.
AddressMode
Addressing modes to use when sampling textures.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
@ Border
Texture color is set to the border color when outside [0, 1] range.
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
@ Mirror
Texture coordinates are mirrored when outside [0, 1] range.