35void apply_texture_conversion(bimg::ImageContainer* image,
const std::string& semantic,
bool inverse);
36void process_raw_texture_data(
const aiTexture* assimp_tex,
const fs::path& output_file,
37 const std::string& semantic,
bool inverse);
38void apply_specular_to_metallic_roughness_conversion(bimg::ImageContainer* image);
39auto convert_specular_gloss_to_metallic_roughness(
const aiColor3D& diffuse_color,
40 const aiColor3D& specular_color,
41 float glossiness_factor) -> std::tuple<aiColor3D, float, float>;
43auto has_rotation_channel(
const aiAnimation* animation,
const std::string& nodeName) ->
bool
50 for(
unsigned int ch = 0; ch < animation->mNumChannels; ++ch)
52 aiNodeAnim* channel = animation->mChannels[ch];
59 if(std::string(channel->mNodeName.C_Str()) == nodeName)
62 if(channel->mNumRotationKeys > 1)
72auto has_rotation_channel(
const aiScene*
scene,
const std::string& nodeName) ->
bool
74 for(
unsigned int animIdx = 0; animIdx <
scene->mNumAnimations; ++animIdx)
76 aiAnimation* animation =
scene->mAnimations[animIdx];
78 if(has_rotation_channel(animation, nodeName))
86auto has_trannslation_channel(
const aiAnimation* animation,
const std::string& nodeName) ->
bool
93 for(
unsigned int ch = 0; ch < animation->mNumChannels; ++ch)
95 aiNodeAnim* channel = animation->mChannels[ch];
102 if(std::string(channel->mNodeName.C_Str()) == nodeName)
105 if(channel->mNumPositionKeys > 1)
117auto has_trannslation_channel(
const aiScene*
scene,
const std::string& nodeName) ->
bool
119 for(
unsigned int animIdx = 0; animIdx <
scene->mNumAnimations; ++animIdx)
121 aiAnimation* animation =
scene->mAnimations[animIdx];
123 if(has_trannslation_channel(animation, nodeName))
131enum channel_requirement
139auto find_first_animated_node_dfs(aiNode* node,
140 const aiScene*
scene,
141 const aiAnimation* animation,
142 channel_requirement req) -> aiNode*
149 case channel_requirement::translation:
151 if(has_trannslation_channel(animation, std::string(node->mName.C_Str())))
156 case channel_requirement::rotation:
158 if(has_rotation_channel(animation, std::string(node->mName.C_Str())))
165 if(has_trannslation_channel(animation, std::string(node->mName.C_Str())))
173 for(
unsigned int i = 0; i < node->mNumChildren; ++i)
175 aiNode* found = find_first_animated_node_dfs(node->mChildren[i],
scene, animation, req);
187auto find_root_motion_node_dfs(
const aiScene*
scene,
const aiAnimation* animation, channel_requirement req) -> aiNode*
194 return find_first_animated_node_dfs(
scene->mRootNode,
scene, animation, req);
198auto find_first_animated_node_bfs(
const aiScene*
scene,
const aiAnimation* animation, channel_requirement req)
206 std::queue<aiNode*> nodeQueue;
207 nodeQueue.push(
scene->mRootNode);
209 while(!nodeQueue.empty())
211 aiNode* current = nodeQueue.front();
216 case channel_requirement::translation:
218 if(has_trannslation_channel(animation, std::string(current->mName.C_Str())))
223 case channel_requirement::rotation:
225 if(has_rotation_channel(animation, std::string(current->mName.C_Str())))
232 if(has_trannslation_channel(animation, std::string(current->mName.C_Str())))
240 if(has_trannslation_channel(animation, std::string(current->mName.C_Str())))
246 for(
unsigned int i = 0; i < current->mNumChildren; ++i)
248 nodeQueue.push(current->mChildren[i]);
257auto find_root_motion_node_bfs(
const aiScene*
scene,
const aiAnimation* animation, channel_requirement req) -> aiNode*
259 return find_first_animated_node_bfs(
scene, animation, req);
263auto interpolate_position(
float animation_time,
const aiNodeAnim* node_anim) -> aiVector3D
265 if(node_anim->mNumPositionKeys == 1)
267 return node_anim->mPositionKeys[0].mValue;
270 for(
unsigned int i = 0; i < node_anim->mNumPositionKeys - 1; ++i)
272 if(animation_time < (
float)node_anim->mPositionKeys[i + 1].mTime)
274 float time1 = (float)node_anim->mPositionKeys[i].mTime;
275 float time2 = (float)node_anim->mPositionKeys[i + 1].mTime;
276 float factor = (animation_time - time1) / (time2 - time1);
277 const aiVector3D& start = node_anim->mPositionKeys[i].mValue;
278 const aiVector3D& end = node_anim->mPositionKeys[i + 1].mValue;
279 aiVector3D delta = end - start;
280 return start + factor * delta;
283 return node_anim->mPositionKeys[0].mValue;
287auto interpolate_rotation(
float animation_time,
const aiNodeAnim* node_anim) -> aiQuaternion
289 if(node_anim->mNumRotationKeys == 1)
291 return node_anim->mRotationKeys[0].mValue;
294 for(
unsigned int i = 0; i < node_anim->mNumRotationKeys - 1; ++i)
296 if(animation_time < (
float)node_anim->mRotationKeys[i + 1].mTime)
298 float time1 = (float)node_anim->mRotationKeys[i].mTime;
299 float time2 = (float)node_anim->mRotationKeys[i + 1].mTime;
300 float factor = (animation_time - time1) / (time2 - time1);
301 const aiQuaternion& start = node_anim->mRotationKeys[i].mValue;
302 const aiQuaternion& end = node_anim->mRotationKeys[i + 1].mValue;
304 aiQuaternion::Interpolate(result, start, end, factor);
305 return result.Normalize();
308 return node_anim->mRotationKeys[0].mValue;
312auto interpolate_scaling(
float animation_time,
const aiNodeAnim* node_anim) -> aiVector3D
314 if(node_anim->mNumScalingKeys == 1)
316 return node_anim->mScalingKeys[0].mValue;
319 for(
unsigned int i = 0; i < node_anim->mNumScalingKeys - 1; ++i)
321 if(animation_time < (
float)node_anim->mScalingKeys[i + 1].mTime)
323 float time1 = (float)node_anim->mScalingKeys[i].mTime;
324 float time2 = (float)node_anim->mScalingKeys[i + 1].mTime;
325 float factor = (animation_time - time1) / (time2 - time1);
326 const aiVector3D& start = node_anim->mScalingKeys[i].mValue;
327 const aiVector3D& end = node_anim->mScalingKeys[i + 1].mValue;
328 aiVector3D delta = end - start;
329 return start + factor * delta;
332 return node_anim->mScalingKeys[0].mValue;
336auto find_node_anim(
const aiAnimation* animation,
const aiString& node_name) ->
const aiNodeAnim*
338 for(
unsigned int i = 0; i < animation->mNumChannels; ++i)
340 const aiNodeAnim* node_anim = animation->mChannels[i];
341 if(std::string(node_anim->mNodeName.C_Str()) == node_name.C_Str())
350auto calculate_bone_transform(
const aiNode* node,
351 const aiString& bone_name,
352 const aiAnimation* animation,
353 float animation_time,
354 const aiMatrix4x4& parent_transform) -> aiMatrix4x4
356 std::string node_name(node->mName.C_Str());
359 const aiNodeAnim* node_anim = find_node_anim(animation, node->mName);
362 aiMatrix4x4 local_transform = node->mTransformation;
368 aiVector3D interpolated_position = interpolate_position(animation_time, node_anim);
369 aiQuaternion interpolated_rotation = interpolate_rotation(animation_time, node_anim);
370 aiVector3D interpolated_scaling = interpolate_scaling(animation_time, node_anim);
373 aiMatrix4x4 position_matrix;
374 aiMatrix4x4::Translation(interpolated_position, position_matrix);
376 aiMatrix4x4 rotation_matrix = aiMatrix4x4(interpolated_rotation.GetMatrix());
378 aiMatrix4x4 scaling_matrix;
379 aiMatrix4x4::Scaling(interpolated_scaling, scaling_matrix);
382 local_transform = position_matrix * rotation_matrix * scaling_matrix;
386 aiMatrix4x4 global_transform = parent_transform * local_transform;
389 if(node_name == bone_name.C_Str())
391 return global_transform;
395 for(
unsigned int i = 0; i < node->mNumChildren; ++i)
397 auto child_transform =
398 calculate_bone_transform(node->mChildren[i], bone_name, animation, animation_time, global_transform);
399 if(child_transform != aiMatrix4x4())
401 return child_transform;
406 return aiMatrix4x4();
409using animation_bounding_box_map = std::unordered_map<const aiAnimation*, std::vector<math::bbox>>;
413 aiVector3D transformed_point = transform *
point;
414 return math::vec3(transformed_point.x, transformed_point.y, transformed_point.z);
417auto get_transformed_vertices(
const aiMesh*
mesh,
418 const aiScene*
scene,
419 float time_in_seconds,
420 const aiAnimation* animation) -> std::vector<math::vec3>
422 std::vector<math::vec3> transformed_vertices(
mesh->mNumVertices, math::vec3(0.0f));
429 [&](
const aiBone* bone)
431 aiMatrix4x4 bone_offset = bone->mOffsetMatrix;
434 aiMatrix4x4 bone_transform =
435 calculate_bone_transform(scene->mRootNode, bone->mName, animation, time_in_seconds, aiMatrix4x4());
438 std::for_each(bone->mWeights,
439 bone->mWeights + bone->mNumWeights,
440 [&](const aiVertexWeight& weight)
442 unsigned int vertex_id = weight.mVertexId;
443 float weight_value = weight.mWeight;
445 aiVector3D position = mesh->mVertices[vertex_id];
446 math::vec3 transformed_pos = transform_point(bone_transform * bone_offset, position);
449 transformed_vertices[vertex_id] += transformed_pos * weight_value;
453 return transformed_vertices;
457auto calculate_bounding_box(
const std::vector<math::vec3>& vertices) ->
math::bbox
462 std::for_each(vertices.begin(),
464 [&](
const math::vec3& vertex)
466 box.add_point(vertex);
473void propagate_bone_influence(
const aiNode* node, std::unordered_set<std::string>& affected_bones)
476 affected_bones.insert(node->mName.C_Str());
479 for(
unsigned int i = 0; i < node->mNumChildren; ++i)
481 propagate_bone_influence(node->mChildren[i], affected_bones);
486auto get_affected_bones_and_children(
const aiScene*
scene,
const aiAnimation* animation)
487 -> std::unordered_set<std::string>
489 std::unordered_set<std::string> affected_bones;
492 for(
unsigned int i = 0; i < animation->mNumChannels; ++i)
494 const aiNodeAnim* node_anim = animation->mChannels[i];
495 affected_bones.insert(node_anim->mNodeName.C_Str());
498 const aiNode* affected_node =
scene->mRootNode->FindNode(node_anim->mNodeName);
501 propagate_bone_influence(affected_node, affected_bones);
505 return affected_bones;
509auto is_mesh_affected_by_animation(
const aiMesh*
mesh,
const std::unordered_set<std::string>& affected_bones) ->
bool
511 for(
unsigned int i = 0; i <
mesh->mNumBones; ++i)
513 if(affected_bones.find(
mesh->mBones[i]->mName.C_Str()) != affected_bones.end())
521auto get_affected_meshes(
const aiScene*
scene,
522 const aiAnimation* animation,
523 const std::unordered_set<std::string>& affected_bones)
525 std::vector<const aiMesh*> affected_meshes;
526 for(
unsigned int mesh_index = 0; mesh_index <
scene->mNumMeshes; ++mesh_index)
528 const aiMesh*
mesh =
scene->mMeshes[mesh_index];
531 if(is_mesh_affected_by_animation(
mesh, affected_bones))
533 affected_meshes.emplace_back(
mesh);
537 return affected_meshes;
541auto compute_bounding_boxes_for_animations(
const aiScene*
scene,
float sample_interval = 0.2f)
542 -> animation_bounding_box_map
546 animation_bounding_box_map animation_bounding_boxes;
548 if(!
scene->HasAnimations())
550 return animation_bounding_boxes;
553 float total_steps = 0;
554 for(
unsigned int anim_index = 0; anim_index <
scene->mNumAnimations; ++anim_index)
556 const aiAnimation* animation =
scene->mAnimations[anim_index];
558 animation_bounding_boxes[animation].clear();
560 float animation_duration = (float)animation->mDuration;
561 float ticks_per_second = (animation->mTicksPerSecond != 0.0f) ? (
float)animation->mTicksPerSecond : 25.0f;
562 float steps = animation_duration / (sample_interval * ticks_per_second);
563 total_steps += steps;
566 std::atomic<size_t> current_steps = 0;
572 [&](
const aiAnimation* animation)
574 float animation_duration = (float)animation->mDuration;
575 float ticks_per_second = (animation->mTicksPerSecond != 0.0f) ? (float)animation->mTicksPerSecond : 25.0f;
576 float steps = animation_duration / (sample_interval * ticks_per_second);
578 auto& boxes = animation_bounding_boxes[animation];
579 boxes.reserve(size_t(steps));
582 auto affected_bones = get_affected_bones_and_children(scene, animation);
583 auto affected_meshes = get_affected_meshes(scene, animation, affected_bones);
588 float percent = (float(current_steps) / total_steps) * 100.0f;
590 for(const auto& mesh : affected_meshes)
593 auto transformed_vertices = get_transformed_vertices(mesh, scene, time, animation);
596 auto frame_bounding_box = calculate_bounding_box(transformed_vertices);
599 frame_bounding_box.inflate(frame_bounding_box.get_extents() * 0.05f);
602 boxes.push_back(frame_bounding_box);
610 return animation_bounding_boxes;
615auto get_texture_extension_from_texture(
const aiTexture* texture) -> std::string
617 if(texture->achFormatHint[0] !=
'\0')
619 return std::string(
".") + texture->achFormatHint;
624auto get_texture_extension(
const aiTexture* texture) -> std::string
626 auto extension = get_texture_extension_from_texture(texture);
628 if(extension ==
".jpg" || extension ==
".jpeg")
636auto get_embedded_texture_name(
const aiTexture* texture,
638 const fs::path& filename,
639 const std::string& semantic) -> std::string
641 return fmt::format(
"[{}] {} {}{}", index, semantic, filename.string(), get_texture_extension(texture));
644auto process_matrix(
const aiMatrix4x4& assimp_matrix) -> math::mat4
648 matrix[0][0] = assimp_matrix.a1;
649 matrix[1][0] = assimp_matrix.a2;
650 matrix[2][0] = assimp_matrix.a3;
651 matrix[3][0] = assimp_matrix.a4;
653 matrix[0][1] = assimp_matrix.b1;
654 matrix[1][1] = assimp_matrix.b2;
655 matrix[2][1] = assimp_matrix.b3;
656 matrix[3][1] = assimp_matrix.b4;
658 matrix[0][2] = assimp_matrix.c1;
659 matrix[1][2] = assimp_matrix.c2;
660 matrix[2][2] = assimp_matrix.c3;
661 matrix[3][2] = assimp_matrix.c4;
663 matrix[0][3] = assimp_matrix.d1;
664 matrix[1][3] = assimp_matrix.d2;
665 matrix[2][3] = assimp_matrix.d3;
666 matrix[3][3] = assimp_matrix.d4;
671void process_vertices(aiMesh* mesh, mesh::load_data& load_data)
673 auto& submesh = load_data.submeshes.back();
676 bool has_position = load_data.vertex_format.has(gfx::attribute::Position);
677 bool has_normal = load_data.vertex_format.has(gfx::attribute::Normal);
678 bool has_bitangent = load_data.vertex_format.has(gfx::attribute::Bitangent);
679 bool has_tangent = load_data.vertex_format.has(gfx::attribute::Tangent);
680 bool has_texcoord0 = load_data.vertex_format.has(gfx::attribute::TexCoord0);
681 auto vertex_stride = load_data.vertex_format.getStride();
683 std::uint32_t current_vertex = load_data.vertex_count;
684 load_data.vertex_count += mesh->mNumVertices;
685 load_data.vertex_data.resize(load_data.vertex_count * vertex_stride);
687 std::uint8_t* current_vertex_ptr = load_data.vertex_data.data() + current_vertex * vertex_stride;
689 for(
size_t i = 0;
i < mesh->mNumVertices; ++
i, current_vertex_ptr += vertex_stride)
692 if(mesh->HasPositions() && has_position)
695 std::memcpy(position, &mesh->mVertices[i],
sizeof(aiVector3D));
697 gfx::vertex_pack(position,
false, gfx::attribute::Position, load_data.vertex_format, current_vertex_ptr);
699 submesh.bbox.add_point(math::vec3(position[0], position[1], position[2]));
703 if(mesh->HasTextureCoords(0) && has_texcoord0)
705 float textureCoords[4];
706 std::memcpy(textureCoords, &mesh->mTextureCoords[0][i],
sizeof(aiVector2D));
710 gfx::attribute::TexCoord0,
711 load_data.vertex_format,
717 if(mesh->HasNormals() && has_normal)
719 std::memcpy(math::value_ptr(
normal), &mesh->mNormals[i],
sizeof(aiVector3D));
723 gfx::attribute::Normal,
724 load_data.vertex_format,
730 if(mesh->HasTangentsAndBitangents() && has_tangent)
732 std::memcpy(math::value_ptr(tangent), &mesh->mTangents[i],
sizeof(aiVector3D));
737 gfx::attribute::Tangent,
738 load_data.vertex_format,
743 math::vec4 bitangent;
744 if(mesh->HasTangentsAndBitangents() && has_bitangent)
746 std::memcpy(math::value_ptr(bitangent), &mesh->mBitangents[i],
sizeof(aiVector3D));
748 math::dot(math::vec3(bitangent), math::normalize(math::cross(math::vec3(
normal), math::vec3(tangent))));
749 tangent.w = handedness;
753 gfx::attribute::Bitangent,
754 load_data.vertex_format,
760void process_faces(aiMesh* mesh, std::uint32_t submesh_offset, mesh::load_data& load_data)
762 load_data.triangle_count += mesh->mNumFaces;
764 load_data.triangle_data.reserve(load_data.triangle_data.size() + mesh->mNumFaces);
766 for(
size_t i = 0;
i < mesh->mNumFaces; ++
i)
768 aiFace face = mesh->mFaces[
i];
770 auto& triangle = load_data.triangle_data.emplace_back();
771 triangle.data_group_id = mesh->mMaterialIndex;
773 auto num_indices = std::min<size_t>(face.mNumIndices, 3);
774 for(
size_t j = 0;
j < num_indices; ++
j)
776 triangle.indices[
j] = face.mIndices[
j] + submesh_offset;
781void process_bones(aiMesh* mesh, std::uint32_t submesh_offset, mesh::load_data& load_data)
785 auto& bone_influences = load_data.skin_data.get_bones();
787 for(
size_t i = 0;
i < mesh->mNumBones; ++
i)
789 aiBone* assimp_bone = mesh->mBones[
i];
790 const std::string bone_name = assimp_bone->mName.C_Str();
792 auto it = std::find_if(std::begin(bone_influences),
793 std::end(bone_influences),
794 [&bone_name](
const auto& bone)
796 return bone_name == bone.bone_id;
799 skin_bind_data::bone_influence* bone_ptr =
nullptr;
800 if(it != std::end(bone_influences))
806 const auto& assimp_matrix = assimp_bone->mOffsetMatrix;
807 skin_bind_data::bone_influence bone_influence;
808 bone_influence.bone_id = bone_name;
809 bone_influence.bind_pose_transform = process_matrix(assimp_matrix);
810 bone_influences.emplace_back(std::move(bone_influence));
811 bone_ptr = &bone_influences.back();
814 if(bone_ptr ==
nullptr)
819 for(
size_t j = 0;
j < assimp_bone->mNumWeights; ++
j)
821 aiVertexWeight assimp_influence = assimp_bone->mWeights[
j];
823 skin_bind_data::vertex_influence influence;
824 influence.vertex_index = assimp_influence.mVertexId + submesh_offset;
825 influence.weight = assimp_influence.mWeight;
827 bone_ptr->influences.emplace_back(influence);
833void process_mesh(aiMesh* mesh, mesh::load_data& load_data)
835 load_data.submeshes.emplace_back();
836 auto& submesh = load_data.submeshes.back();
837 submesh.vertex_start = load_data.vertex_count;
838 submesh.vertex_count = mesh->mNumVertices;
839 submesh.face_start = load_data.triangle_count;
840 submesh.face_count = mesh->mNumFaces;
841 submesh.data_group_id = mesh->mMaterialIndex;
842 submesh.skinned = mesh->HasBones();
843 load_data.material_count = std::max(load_data.material_count, submesh.data_group_id + 1);
845 process_faces(mesh, submesh.vertex_start, load_data);
846 process_bones(mesh, submesh.vertex_start, load_data);
847 process_vertices(mesh, load_data);
850void process_meshes(
const aiScene* scene, mesh::load_data& load_data)
852 for(
size_t i = 0;
i < scene->mNumMeshes; ++
i)
854 aiMesh* mesh = scene->mMeshes[
i];
855 process_mesh(mesh, load_data);
859void process_node(
const aiScene* scene,
860 mesh::load_data& load_data,
862 const std::unique_ptr<mesh::armature_node>& armature_node,
864 std::unordered_map<std::string, unsigned int>& node_to_index_lut)
866 armature_node->name = node->mName.C_Str();
867 armature_node->local_transform = process_matrix(node->mTransformation);
868 armature_node->children.resize(node->mNumChildren);
869 armature_node->index = node_to_index_lut[armature_node->name];
870 auto resolved_transform = parent_transform * armature_node->local_transform;
872 for(uint32_t i = 0;
i < node->mNumMeshes; ++
i)
874 uint32_t submesh_index = node->mMeshes[
i];
875 armature_node->submeshes.emplace_back(submesh_index);
877 auto& submesh = load_data.submeshes[submesh_index];
878 submesh.node_id = node->mName.C_Str();
880 auto transformed_bbox =
math::bbox::mul(submesh.bbox, resolved_transform);
881 load_data.bbox.add_point(transformed_bbox.min);
882 load_data.bbox.add_point(transformed_bbox.max);
885 for(
size_t i = 0;
i < node->mNumChildren; ++
i)
887 armature_node->children[
i] = std::make_unique<mesh::armature_node>();
891 armature_node->children[i],
897void process_nodes(
const aiScene* scene,
898 mesh::load_data& load_data,
899 std::unordered_map<std::string, unsigned int>& node_to_index_lut)
902 if(scene->mRootNode !=
nullptr)
905 load_data.root_node = std::make_unique<mesh::armature_node>();
914 auto get_axis = [&](
const std::string&
name, math::vec3 fallback)
916 if(!scene->mMetaData)
922 if(!scene->mMetaData->Get<
int>(
name, axis))
927 if(!scene->mMetaData->Get<
int>(
name +
"Sign", axis_sign))
931 math::vec3 result{0.0f, 0.0f, 0.0f};
933 if(axis < 0 || axis >= 3)
938 result[
axis] = float(axis_sign);
942 auto x_axis = get_axis(
"CoordAxis", {1.0f, 0.0f, 0.0f});
943 auto y_axis = get_axis(
"UpAxis", {0.0f, 1.0f, 0.0f});
944 auto z_axis = get_axis(
"FrontAxis", {0.0f, 0.0f, 1.0f});
949void dfs_assign_indices(
const aiNode* node,
950 std::unordered_map<std::string, unsigned int>& node_indices,
951 unsigned int& current_index)
954 node_indices[node->mName.C_Str()] = current_index;
960 for(
unsigned int i = 0;
i < node->mNumChildren; ++
i)
962 dfs_assign_indices(node->mChildren[i], node_indices, current_index);
966auto assign_node_indices(
const aiScene* scene) -> std::unordered_map<std::string, unsigned int>
968 std::unordered_map<std::string, unsigned int> node_indices;
969 unsigned int current_index = 0;
974 dfs_assign_indices(scene->mRootNode, node_indices, current_index);
980auto is_node_a_bone(
const std::string& node_name,
const aiScene* scene) ->
bool
982 for(
unsigned int i = 0;
i < scene->mNumMeshes; ++
i)
984 const aiMesh* mesh = scene->mMeshes[
i];
985 for(
unsigned int j = 0;
j < mesh->mNumBones; ++
j)
987 if(mesh->mBones[j]->mName.C_Str() == node_name)
996auto is_node_a_parent_of_bone(
const std::string& node_name,
const aiScene* scene) ->
bool
998 for(
unsigned int i = 0;
i < scene->mNumMeshes; ++
i)
1000 const aiMesh* mesh = scene->mMeshes[
i];
1001 for(
unsigned int j = 0;
j < mesh->mNumBones; ++
j)
1003 const aiNode* bone_node = scene->mRootNode->FindNode(mesh->mBones[j]->mName);
1004 const aiNode* current_node = bone_node;
1006 while(current_node !=
nullptr)
1008 if(current_node->mName.C_Str() == node_name)
1012 current_node = current_node->mParent;
1019auto is_node_a_submesh(
const std::string& node_name,
const aiScene* scene) ->
bool
1021 const aiNode* node = scene->mRootNode->FindNode(node_name.c_str());
1022 return node !=
nullptr && node->mNumMeshes > 0;
1025auto is_node_a_parent_of_submesh(
const std::string& node_name,
const aiScene* scene) ->
bool
1027 const aiNode* root = scene->mRootNode;
1029 for(
unsigned int i = 0;
i < scene->mNumMeshes; ++
i)
1031 const aiMesh* mesh = scene->mMeshes[
i];
1032 const aiNode* submesh_node = root->FindNode(mesh->mName);
1033 const aiNode* current_node = submesh_node;
1035 while(current_node !=
nullptr)
1037 if(current_node->mName.C_Str() == node_name)
1041 current_node = current_node->mParent;
1047void process_animation(
const aiScene* scene,
1048 const fs::path& filename,
1049 const aiAnimation* assimp_anim,
1050 mesh::load_data& load_data,
1051 std::unordered_map<std::string, unsigned int>& node_to_index_lut,
1052 animation_clip& anim)
1054 auto fixed_name = filename.string() +
"_" +
string_utils::replace(assimp_anim->mName.C_Str(),
".",
"_");
1055 anim.name = fixed_name;
1056 auto ticks_per_second = assimp_anim->mTicksPerSecond;
1057 if(ticks_per_second < 0.001)
1059 ticks_per_second = 25.0;
1062 auto ticks = assimp_anim->mDuration;
1064 anim.duration =
decltype(anim.duration)(ticks / ticks_per_second);
1066 if(assimp_anim->mNumChannels > 0)
1068 anim.channels.reserve(assimp_anim->mNumChannels);
1070 bool needs_sort =
false;
1073 for(
size_t i = 0;
i < assimp_anim->mNumChannels; ++
i)
1075 const aiNodeAnim* assimp_node_anim = assimp_anim->mChannels[
i];
1077 bool is_bone = is_node_a_bone(assimp_node_anim->mNodeName.C_Str(), scene);
1078 bool is_parent_of_bone = is_node_a_parent_of_bone(assimp_node_anim->mNodeName.C_Str(), scene);
1079 bool is_submesh = is_node_a_submesh(assimp_node_anim->mNodeName.C_Str(), scene);
1080 bool is_parent_of_submesh = is_node_a_parent_of_submesh(assimp_node_anim->mNodeName.C_Str(), scene);
1082 bool is_relevant = is_bone || is_parent_of_bone || is_submesh || is_parent_of_submesh;
1091 auto& node_anim = anim.channels.emplace_back();
1092 node_anim.node_name = assimp_node_anim->mNodeName.C_Str();
1093 node_anim.node_index = node_to_index_lut[node_anim.node_name];
1094 if(!needs_sort && anim.channels.size() > 1)
1096 auto& prev_node_anim = anim.channels[anim.channels.size() - 2];
1097 if(node_anim.node_index < prev_node_anim.node_index)
1103 if(assimp_node_anim->mNumPositionKeys > 0)
1105 node_anim.position_keys.resize(assimp_node_anim->mNumPositionKeys);
1108 for(
size_t idx = 0; idx < assimp_node_anim->mNumPositionKeys; ++idx)
1110 const auto& anim_key = assimp_node_anim->mPositionKeys[idx];
1111 auto&
key = node_anim.position_keys[idx];
1112 key.time =
decltype(
key.time)(anim_key.mTime / ticks_per_second);
1113 key.value.x = anim_key.mValue.x;
1114 key.value.y = anim_key.mValue.y;
1115 key.value.z = anim_key.mValue.z;
1118 if(assimp_node_anim->mNumRotationKeys > 0)
1120 node_anim.rotation_keys.resize(assimp_node_anim->mNumRotationKeys);
1123 for(
size_t idx = 0; idx < assimp_node_anim->mNumRotationKeys; ++idx)
1125 const auto& anim_key = assimp_node_anim->mRotationKeys[idx];
1126 auto&
key = node_anim.rotation_keys[idx];
1127 key.time =
decltype(
key.time)(anim_key.mTime / ticks_per_second);
1128 key.value.x = anim_key.mValue.x;
1129 key.value.y = anim_key.mValue.y;
1130 key.value.z = anim_key.mValue.z;
1131 key.value.w = anim_key.mValue.w;
1134 if(assimp_node_anim->mNumScalingKeys > 0)
1136 node_anim.scaling_keys.resize(assimp_node_anim->mNumScalingKeys);
1139 for(
size_t idx = 0; idx < assimp_node_anim->mNumScalingKeys; ++idx)
1141 const auto& anim_key = assimp_node_anim->mScalingKeys[idx];
1142 auto&
key = node_anim.scaling_keys[idx];
1143 key.time =
decltype(
key.time)(anim_key.mTime / ticks_per_second);
1144 key.value.x = anim_key.mValue.x;
1145 key.value.y = anim_key.mValue.y;
1146 key.value.z = anim_key.mValue.z;
1150 auto root_motion_translation_candidate =
1151 find_root_motion_node_bfs(scene, assimp_anim, channel_requirement::translation);
1152 auto root_motion_rotation_candidate = find_root_motion_node_bfs(scene, assimp_anim, channel_requirement::rotation);
1154 if(root_motion_translation_candidate)
1156 anim.root_motion.position_node_name = root_motion_translation_candidate->mName.C_Str();
1157 anim.root_motion.position_node_index = node_to_index_lut[anim.root_motion.position_node_name];
1159 if(root_motion_rotation_candidate)
1161 anim.root_motion.rotation_node_name = root_motion_rotation_candidate->mName.C_Str();
1162 anim.root_motion.rotation_node_index = node_to_index_lut[anim.root_motion.rotation_node_name];
1167 std::sort(anim.channels.begin(),
1168 anim.channels.end(),
1169 [](
const auto& lhs,
const auto& rhs)
1171 return lhs.node_index < rhs.node_index;
1175 APPLOG_TRACE(
"Mesh Importer : Animation {} discarded {} non relevat node keys", anim.name, skipped);
1177void process_animations(
const aiScene* scene,
1178 const fs::path& filename,
1179 mesh::load_data& load_data,
1180 std::unordered_map<std::string, unsigned int>& node_to_index_lut,
1181 std::vector<animation_clip>& animations)
1183 if(scene->mNumAnimations > 0)
1185 animations.resize(scene->mNumAnimations);
1188 for(
size_t i = 0;
i < scene->mNumAnimations; ++
i)
1190 const aiAnimation* assimp_anim = scene->mAnimations[
i];
1191 auto& anim = animations[
i];
1192 process_animation(scene, filename, assimp_anim, load_data, node_to_index_lut, anim);
1196void process_embedded_texture(
const aiTexture* assimp_tex,
1197 size_t assimp_tex_idx,
1198 const fs::path& filename,
1199 const fs::path& output_dir,
1200 std::vector<imported_texture>& textures)
1202 imported_texture texture{};
1203 auto it = std::find_if(std::begin(textures),
1205 [&](
const imported_texture& texture)
1207 return texture.embedded_index == assimp_tex_idx;
1209 if(it != std::end(textures))
1211 if(it->process_count > 0)
1216 it->process_count++;
1219 else if(assimp_tex->mFilename.length > 0)
1221 texture.name = fs::path(assimp_tex->mFilename.C_Str()).filename().string();
1225 texture.name = get_embedded_texture_name(assimp_tex, assimp_tex_idx, filename,
"Texture");
1228 fs::path output_file = output_dir / texture.name;
1230 if(assimp_tex->pcData)
1232 bool compressed = assimp_tex->mHeight == 0;
1233 bool raw = assimp_tex->mHeight > 0;
1238 size_t texture_size = assimp_tex->mWidth;
1241 bimg::ImageContainer* image =
imageLoad(assimp_tex->pcData,
static_cast<uint32_t
>(texture_size));
1245 apply_texture_conversion(image, texture.semantic, texture.inverse);
1247 imageSave(output_file.string().c_str(), image);
1249 bimg::imageFree(image);
1256 process_raw_texture_data(assimp_tex, output_file, texture.semantic, texture.inverse);
1264namespace pixel_transforms
1269 template<
typename TransformFunc>
1270 void transform_pixel(uint8_t* pixel_data, uint32_t bytes_per_pixel, TransformFunc transform_func)
1272 if (bytes_per_pixel >= 4)
1275 float r = pixel_data[0] / 255.0f;
1276 float g = pixel_data[1] / 255.0f;
1277 float b = pixel_data[2] / 255.0f;
1278 float a = pixel_data[3] / 255.0f;
1280 auto [new_r, new_g, new_b, new_a] = transform_func(r, g,
b,
a);
1282 pixel_data[0] =
static_cast<uint8_t
>(math::clamp(new_r, 0.0f, 1.0f) * 255.0f);
1283 pixel_data[1] =
static_cast<uint8_t
>(math::clamp(new_g, 0.0f, 1.0f) * 255.0f);
1284 pixel_data[2] =
static_cast<uint8_t
>(math::clamp(new_b, 0.0f, 1.0f) * 255.0f);
1285 pixel_data[3] =
static_cast<uint8_t
>(math::clamp(new_a, 0.0f, 1.0f) * 255.0f);
1287 else if (bytes_per_pixel >= 3)
1290 float r = pixel_data[0] / 255.0f;
1291 float g = pixel_data[1] / 255.0f;
1292 float b = pixel_data[2] / 255.0f;
1295 auto [new_r, new_g, new_b, new_a] = transform_func(r, g,
b,
a);
1297 pixel_data[0] =
static_cast<uint8_t
>(math::clamp(new_r, 0.0f, 1.0f) * 255.0f);
1298 pixel_data[1] =
static_cast<uint8_t
>(math::clamp(new_g, 0.0f, 1.0f) * 255.0f);
1299 pixel_data[2] =
static_cast<uint8_t
>(math::clamp(new_b, 0.0f, 1.0f) * 255.0f);
1301 else if (bytes_per_pixel == 2)
1304 float luminance = pixel_data[0] / 255.0f;
1305 float a = pixel_data[1] / 255.0f;
1307 auto [new_r, new_g, new_b, new_a] = transform_func(luminance, luminance, luminance,
a);
1309 pixel_data[0] =
static_cast<uint8_t
>(math::clamp(new_r, 0.0f, 1.0f) * 255.0f);
1310 pixel_data[1] =
static_cast<uint8_t
>(math::clamp(new_a, 0.0f, 1.0f) * 255.0f);
1312 else if (bytes_per_pixel == 1)
1315 float luminance = pixel_data[0] / 255.0f;
1317 auto [new_r, new_g, new_b, new_a] = transform_func(luminance, luminance, luminance, 1.0f);
1319 pixel_data[0] =
static_cast<uint8_t
>(math::clamp(new_r, 0.0f, 1.0f) * 255.0f);
1326 auto specular_to_metallic_pixel(
float r,
float g,
float b,
float a) -> std::tuple<float, float, float, float>
1329 float max_specular = std::max({r, g,
b});
1330 float avg_specular = (r + g +
b) / 3.0f;
1331 float color_variance = std::abs(r - avg_specular) + std::abs(g - avg_specular) + std::abs(
b - avg_specular);
1333 float metallic = 0.0f;
1334 const float dielectric_f0 = 0.04f;
1336 if (max_specular <= dielectric_f0)
1340 else if (max_specular >= 0.9f)
1347 float normalized_specular = (max_specular - dielectric_f0) / (1.0f - dielectric_f0);
1348 metallic = math::clamp(normalized_specular, 0.0f, 1.0f);
1351 if (color_variance > 0.1f && avg_specular > 0.3f)
1353 metallic = std::max(metallic, 0.8f);
1358 return std::make_tuple(metallic, metallic, metallic, 1.0f);
1364 auto gloss_to_roughness_pixel(
float r,
float g,
float b,
float a) -> std::tuple<float, float, float, float>
1369 if (std::abs(r - g) < 0.01f && std::abs(g -
b) < 0.01f)
1372 float roughness = 1.0f - r;
1373 return std::make_tuple(roughness, roughness, roughness,
a);
1380 float roughness = 1.0f -
a;
1381 return std::make_tuple(r, g,
b, roughness);
1386 float roughness = 1.0f - g;
1387 return std::make_tuple(r, roughness,
b,
a);
1395 auto specular_to_roughness_pixel(
float r,
float g,
float b,
float a) -> std::tuple<float, float, float, float>
1397 float roughness = 0.0f;
1402 roughness = 1.0f -
a;
1407 float specular_intensity = (r + g +
b) / 3.0f;
1408 roughness = 1.0f - specular_intensity;
1411 return std::make_tuple(roughness, roughness, roughness, 1.0f);
1417 auto specular_to_metallic_roughness_pixel(
float r,
float g,
float b,
float a) -> std::tuple<float, float, float, float>
1420 float max_specular = std::max({r, g,
b});
1421 float avg_specular = (r + g +
b) / 3.0f;
1422 float color_variance = std::abs(r - avg_specular) + std::abs(g - avg_specular) + std::abs(
b - avg_specular);
1424 float metallic = 0.0f;
1425 const float dielectric_f0 = 0.04f;
1427 if (max_specular <= dielectric_f0)
1431 else if (max_specular >= 0.9f)
1438 float normalized_specular = (max_specular - dielectric_f0) / (1.0f - dielectric_f0);
1439 metallic = math::clamp(normalized_specular, 0.0f, 1.0f);
1442 if (color_variance > 0.1f && avg_specular > 0.3f)
1444 metallic = std::max(metallic, 0.8f);
1449 float roughness = 0.0f;
1452 roughness = 1.0f -
a;
1457 roughness = 1.0f - avg_specular;
1461 return std::make_tuple(1.0f, roughness, metallic, 1.0f);
1467 auto simple_invert_pixel(
float r,
float g,
float b,
float a) -> std::tuple<float, float, float, float>
1469 return std::make_tuple(1.0f - r, 1.0f - g, 1.0f -
b, 1.0f -
a);
1476void apply_texture_conversion(bimg::ImageContainer* image,
const std::string& semantic,
bool inverse)
1478 if(!image || !image->m_data)
1483 uint8_t* image_data =
static_cast<uint8_t*
>(image->m_data);
1484 uint32_t pixel_count = image->m_width * image->m_height;
1485 uint32_t bpp = bimg::getBitsPerPixel(image->m_format);
1486 uint32_t bytes_per_pixel = bpp / 8;
1488 if(semantic ==
"SpecularToMetallicRoughness")
1491 apply_specular_to_metallic_roughness_conversion(image);
1494 else if(semantic ==
"GlossToRoughness")
1496 for(uint32_t i = 0; i < pixel_count; ++i)
1498 uint32_t pixel_index = i * bytes_per_pixel;
1499 pixel_transforms::transform_pixel(&image_data[pixel_index], bytes_per_pixel,
1500 pixel_transforms::gloss_to_roughness_pixel);
1502 APPLOG_TRACE(
"Mesh Importer: Applied GlossToRoughness conversion to texture");
1504 else if(semantic ==
"SpecularToRoughness")
1506 for(uint32_t i = 0;
i < pixel_count; ++
i)
1508 uint32_t pixel_index =
i * bytes_per_pixel;
1509 pixel_transforms::transform_pixel(&image_data[pixel_index], bytes_per_pixel,
1510 pixel_transforms::specular_to_roughness_pixel);
1512 APPLOG_TRACE(
"Mesh Importer: Applied SpecularToRoughness conversion to texture");
1514 else if(semantic ==
"SpecularToMetallic")
1516 for(uint32_t i = 0;
i < pixel_count; ++
i)
1518 uint32_t pixel_index =
i * bytes_per_pixel;
1519 pixel_transforms::transform_pixel(&image_data[pixel_index], bytes_per_pixel,
1520 pixel_transforms::specular_to_metallic_pixel);
1522 APPLOG_TRACE(
"Mesh Importer: Applied SpecularToMetallic conversion to texture");
1524 else if(semantic ==
"ExtractMetallicChannel")
1527 for(uint32_t i = 0;
i < pixel_count; ++
i)
1529 uint32_t pixel_index =
i * bytes_per_pixel;
1530 pixel_transforms::transform_pixel(&image_data[pixel_index], bytes_per_pixel,
1531 [](
float r,
float g,
float b,
float a) {
1533 return std::make_tuple(
b,
b,
b, 1.0f);
1536 APPLOG_TRACE(
"Mesh Importer: Extracted metallic channel for debugging");
1538 else if(semantic ==
"ExtractRoughnessChannel")
1541 for(uint32_t i = 0;
i < pixel_count; ++
i)
1543 uint32_t pixel_index =
i * bytes_per_pixel;
1544 pixel_transforms::transform_pixel(&image_data[pixel_index], bytes_per_pixel,
1545 [](
float r,
float g,
float b,
float a) {
1547 return std::make_tuple(g, g, g, 1.0f);
1550 APPLOG_TRACE(
"Mesh Importer: Extracted roughness channel for debugging");
1555 for(uint32_t i = 0;
i < pixel_count; ++
i)
1557 uint32_t pixel_index =
i * bytes_per_pixel;
1558 pixel_transforms::transform_pixel(&image_data[pixel_index], bytes_per_pixel,
1559 pixel_transforms::simple_invert_pixel);
1561 APPLOG_TRACE(
"Mesh Importer: Applied simple inversion to texture");
1568void apply_specular_to_metallic_roughness_conversion(bimg::ImageContainer* image)
1570 if(!image || !image->m_data)
1575 uint8_t* image_data =
static_cast<uint8_t*
>(image->m_data);
1576 uint32_t pixel_count = image->m_width * image->m_height;
1577 uint32_t bpp = bimg::getBitsPerPixel(image->m_format);
1578 uint32_t bytes_per_pixel = bpp / 8;
1580 for(uint32_t i = 0;
i < pixel_count; ++
i)
1582 uint32_t pixel_index =
i * bytes_per_pixel;
1583 pixel_transforms::transform_pixel(&image_data[pixel_index], bytes_per_pixel,
1584 pixel_transforms::specular_to_metallic_roughness_pixel);
1587 APPLOG_TRACE(
"Mesh Importer: Applied SpecularToMetallicRoughness conversion to texture");
1593void process_raw_texture_data(
const aiTexture* assimp_tex,
const fs::path& output_file,
1594 const std::string& semantic,
bool inverse)
1597 uint32_t width = assimp_tex->mWidth;
1598 uint32_t height = assimp_tex->mHeight;
1601 std::vector<uint8_t> data(width * height * 4);
1602 std::memcpy(data.data(), assimp_tex->pcData, width * height * 4);
1605 if(semantic ==
"GlossToRoughness" || semantic ==
"SpecularToRoughness" || semantic ==
"SpecularToMetallic" || semantic ==
"SpecularToMetallicRoughness")
1608 bimg::ImageContainer image;
1609 image.m_data = data.data();
1610 image.m_width = width;
1611 image.m_height = height;
1613 image.m_format = bimg::TextureFormat::RGBA8;
1614 image.m_numMips = 1;
1615 image.m_hasAlpha =
true;
1617 apply_texture_conversion(&image, semantic, inverse);
1622 for(
size_t i = 0;
i < data.size(); ++
i)
1624 data[
i] = 255 - data[
i];
1629 bx::FileWriter writer;
1632 if(bx::open(&writer, output_file.string().c_str(),
false, &err))
1634 bimg::imageWriteTga(&writer,
1647void log_prop_value(aiMaterialProperty* prop,
const char* name1)
1649 auto data = (T*)prop->mData;
1651 auto count = prop->mDataLength /
sizeof(T);
1659 std::vector<T> vals(
count);
1660 std::memcpy(vals.data(), data,
count *
sizeof(T));
1665void log_materials(
const aiMaterial*
material)
1667 for(uint32_t i = 0;
i <
material->mNumProperties;
i++)
1674 if(prop->mDataLength > 0 && prop->mData)
1676 auto semantic = aiTextureType(prop->mSemantic);
1677 if(semantic != aiTextureType_NONE && semantic != aiTextureType_UNKNOWN)
1679 APPLOG_TRACE(
" semantic = {0}", aiTextureTypeToString(semantic));
1684 case aiPropertyTypeInfo::aiPTI_Float:
1686 log_prop_value<float>(prop,
"float");
1690 case aiPropertyTypeInfo::aiPTI_Double:
1692 log_prop_value<double>(prop,
"double");
1695 case aiPropertyTypeInfo::aiPTI_Integer:
1697 log_prop_value<int32_t>(prop,
"int");
1701 case aiPropertyTypeInfo::aiPTI_Buffer:
1703 log_prop_value<uint8_t>(prop,
"buffer");
1706 case aiPropertyTypeInfo::aiPTI_String:
1709 if(aiGetMaterialString(
material, prop->mKey.C_Str(), prop->mSemantic, prop->mIndex, &str) ==
1726enum class material_workflow
1736auto detect_duplicate_specular_usage(
const aiMaterial*
material, material_workflow workflow) ->
bool
1738 if(workflow != material_workflow::specular_gloss)
1744 bool has_metallic_texture = (
material->GetTextureCount(aiTextureType_METALNESS) > 0) ||
1745 (
material->GetTextureCount(aiTextureType_GLTF_METALLIC_ROUGHNESS) > 0);
1746 bool has_roughness_texture = (
material->GetTextureCount(aiTextureType_DIFFUSE_ROUGHNESS) > 0) ||
1747 (
material->GetTextureCount(aiTextureType_GLTF_METALLIC_ROUGHNESS) > 0);
1748 bool has_glossiness_texture = (
material->GetTextureCount(aiTextureType_SHININESS) > 0);
1751 if(has_metallic_texture || has_roughness_texture || has_glossiness_texture)
1757 bool has_specular_texture = (
material->GetTextureCount(aiTextureType_SPECULAR) > 0);
1759 if(has_specular_texture)
1766 APPLOG_TRACE(
"Mesh Importer: Detected duplicate specular usage - same texture would be used for both metallic and roughness conversion");
1776auto detect_material_workflow(
const aiMaterial*
material) -> material_workflow
1779 bool has_metallic_factor =
false;
1780 bool has_roughness_factor =
false;
1781 bool has_base_color_factor =
false;
1782 bool has_metallic_texture =
false;
1783 bool has_roughness_texture =
false;
1784 bool has_metallic_roughness_texture =
false;
1785 bool has_base_color_texture =
false;
1788 bool has_specular_factor =
false;
1789 bool has_glossiness_factor =
false;
1790 bool has_diffuse_color =
false;
1791 bool has_specular_color =
false;
1792 bool has_specular_texture =
false;
1793 bool has_glossiness_texture =
false;
1794 bool has_diffuse_texture =
false;
1797 bool has_shininess =
false;
1798 bool has_reflectivity =
false;
1800 ai_real dummy_value{};
1801 aiColor3D dummy_color{};
1805 has_metallic_factor =
material->Get(AI_MATKEY_METALLIC_FACTOR, dummy_value) == AI_SUCCESS;
1806 has_roughness_factor =
material->Get(AI_MATKEY_ROUGHNESS_FACTOR, dummy_value) == AI_SUCCESS;
1807 has_base_color_factor =
material->Get(AI_MATKEY_BASE_COLOR, dummy_color) == AI_SUCCESS;
1810 has_specular_factor =
material->Get(AI_MATKEY_SPECULAR_FACTOR, dummy_value) == AI_SUCCESS;
1811 has_glossiness_factor =
material->Get(AI_MATKEY_GLOSSINESS_FACTOR, dummy_value) == AI_SUCCESS;
1812 has_diffuse_color =
material->Get(AI_MATKEY_COLOR_DIFFUSE, dummy_color) == AI_SUCCESS;
1813 has_specular_color =
material->Get(AI_MATKEY_COLOR_SPECULAR, dummy_color) == AI_SUCCESS;
1816 has_shininess =
material->Get(AI_MATKEY_SHININESS, dummy_value) == AI_SUCCESS;
1817 has_reflectivity =
material->Get(AI_MATKEY_REFLECTIVITY, dummy_value) == AI_SUCCESS;
1820 has_metallic_roughness_texture =
material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path) == AI_SUCCESS;
1821 has_metallic_texture =
material->GetTexture(AI_MATKEY_METALLIC_TEXTURE, &path) == AI_SUCCESS;
1822 has_roughness_texture =
material->GetTexture(AI_MATKEY_ROUGHNESS_TEXTURE, &path) == AI_SUCCESS;
1823 has_base_color_texture =
material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path) == AI_SUCCESS;
1826 has_specular_texture =
material->GetTexture(aiTextureType_SPECULAR, 0, &path) == AI_SUCCESS;
1827 has_glossiness_texture =
material->GetTexture(aiTextureType_SHININESS, 0, &path) == AI_SUCCESS;
1828 has_diffuse_texture =
material->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS;
1831 int metallic_roughness_score = 0;
1832 int specular_gloss_score = 0;
1835 if(has_metallic_factor) metallic_roughness_score += 8;
1836 if(has_roughness_factor) metallic_roughness_score += 8;
1837 if(has_base_color_factor) metallic_roughness_score += 4;
1838 if(has_metallic_roughness_texture) metallic_roughness_score += 12;
1839 if(has_metallic_texture) metallic_roughness_score += 10;
1840 if(has_roughness_texture) metallic_roughness_score += 6;
1841 if(has_base_color_texture) metallic_roughness_score += 3;
1844 if(has_specular_factor) specular_gloss_score += 8;
1845 if(has_glossiness_factor) specular_gloss_score += 8;
1846 if(has_diffuse_color) specular_gloss_score += 4;
1847 if(has_specular_color) specular_gloss_score += 6;
1848 if(has_specular_texture) specular_gloss_score += 10;
1849 if(has_glossiness_texture) specular_gloss_score += 10;
1850 if(has_diffuse_texture) specular_gloss_score += 6;
1853 if(has_shininess) specular_gloss_score += 4;
1854 if(has_reflectivity) specular_gloss_score += 3;
1858 if(has_metallic_factor && has_roughness_factor)
1860 metallic_roughness_score += 5;
1864 if(has_specular_texture && has_diffuse_texture)
1866 specular_gloss_score += 8;
1869 if(has_specular_color && has_diffuse_color)
1871 specular_gloss_score += 6;
1875 APPLOG_TRACE(
"Mesh Importer: Material workflow detection scores - Metallic/Roughness: {}, Specular/Gloss: {}",
1876 metallic_roughness_score, specular_gloss_score);
1879 if(metallic_roughness_score > specular_gloss_score && metallic_roughness_score >= 5)
1881 return material_workflow::metallic_roughness;
1883 else if(specular_gloss_score >= 5)
1885 return material_workflow::specular_gloss;
1888 return material_workflow::unknown;
1894auto convert_specular_to_metallic(
float specular) ->
float
1897 const float dielectric_f0 = 0.04f;
1899 if (specular <= dielectric_f0)
1903 else if (specular >= 0.9f)
1910 float normalized_specular = (specular - dielectric_f0) / (1.0f - dielectric_f0);
1911 return math::clamp(normalized_specular, 0.0f, 1.0f);
1918auto is_specular_color_metallic(
const aiColor3D& specular_color) ->
bool
1922 float avg_specular = (specular_color.r + specular_color.g + specular_color.b) / 3.0f;
1923 float color_variance = std::abs(specular_color.r - avg_specular) +
1924 std::abs(specular_color.g - avg_specular) +
1925 std::abs(specular_color.b - avg_specular);
1928 return color_variance > 0.1f && avg_specular > 0.3f;
1934auto convert_specular_color_to_base_color(
const aiColor3D& specular_color,
float metallic) -> aiColor3D
1939 return specular_color;
1944 return aiColor3D(1.0f, 1.0f, 1.0f);
1951template<
typename GetTextureFunc>
1952auto get_workflow_aware_texture(
const aiMaterial*
material,
1953 material_workflow workflow,
1954 const std::string& target_semantic,
1955 imported_texture& tex,
1956 GetTextureFunc get_imported_texture,
1957 bool use_combined_specular =
false) ->
bool
1959 if(target_semantic ==
"BaseColor")
1962 if(get_imported_texture(
material, AI_MATKEY_BASE_COLOR_TEXTURE,
"BaseColor", tex))
1966 else if(get_imported_texture(
material, aiTextureType_DIFFUSE, 0,
"BaseColor", tex))
1971 else if(target_semantic ==
"Metallic")
1974 if(get_imported_texture(
material, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE,
"MetallicRoughness", tex))
1979 else if(get_imported_texture(
material, AI_MATKEY_METALLIC_TEXTURE,
"Metallic", tex))
1984 else if(workflow == material_workflow::specular_gloss)
1986 if(use_combined_specular)
1991 else if(get_imported_texture(
material, aiTextureType_SPECULAR, 0,
"SpecularToMetallic", tex))
1993 tex.inverse =
false;
1998 else if(target_semantic ==
"Roughness")
2001 if(get_imported_texture(
material, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE,
"MetallicRoughness", tex))
2006 else if(get_imported_texture(
material, AI_MATKEY_ROUGHNESS_TEXTURE,
"Roughness", tex))
2011 else if(workflow == material_workflow::specular_gloss)
2013 if(use_combined_specular)
2018 else if(get_imported_texture(
material, aiTextureType_SHININESS, 0,
"GlossToRoughness", tex))
2024 else if(get_imported_texture(
material, aiTextureType_SPECULAR, 0,
"SpecularToRoughness", tex))
2039void process_material_with_workflow_conversion(
const aiMaterial*
material,
2040 material_workflow workflow,
2041 aiColor3D& base_color,
2046 bool has_base_color = (
material->Get(AI_MATKEY_BASE_COLOR, base_color) == AI_SUCCESS);
2047 bool has_metallic = (
material->Get(AI_MATKEY_METALLIC_FACTOR, metallic) == AI_SUCCESS);
2048 bool has_roughness = (
material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness) == AI_SUCCESS);
2053 if (!has_base_color)
2056 if (
material->Get(AI_MATKEY_COLOR_DIFFUSE, base_color) != AI_SUCCESS)
2058 base_color = aiColor3D{1.0f, 1.0f, 1.0f};
2062 if (workflow == material_workflow::specular_gloss)
2064 aiColor3D diffuse_color = base_color;
2065 aiColor3D specular_color{0.04f, 0.04f, 0.04f};
2066 float specular_factor = 1.0f;
2068 material->Get(AI_MATKEY_COLOR_SPECULAR, specular_color);
2069 material->Get(AI_MATKEY_SPECULAR_FACTOR, specular_factor);
2071 specular_color.r *= specular_factor;
2072 specular_color.g *= specular_factor;
2073 specular_color.b *= specular_factor;
2076 float glossiness = 0.5f;
2077 if(
material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness) != AI_SUCCESS)
2079 float shininess = 32.0f;
2080 if(
material->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS)
2082 glossiness = math::clamp(std::sqrt((shininess + 2.0f) / 1024.0f), 0.0f, 1.0f);
2086 auto [converted_base_color, _, __] =
2087 convert_specular_gloss_to_metallic_roughness(diffuse_color, specular_color, glossiness);
2088 base_color = converted_base_color;
2090 APPLOG_TRACE(
"Mesh Importer: Converted base color from specular/diffuse workflow");
2097 if (workflow == material_workflow::specular_gloss)
2100 aiColor3D diffuse_color = base_color;
2101 aiColor3D specular_color{0.04f, 0.04f, 0.04f};
2102 float specular_factor = 1.0f;
2103 float glossiness = 0.5f;
2105 material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse_color);
2106 material->Get(AI_MATKEY_COLOR_SPECULAR, specular_color);
2107 material->Get(AI_MATKEY_SPECULAR_FACTOR, specular_factor);
2109 specular_color.r *= specular_factor;
2110 specular_color.g *= specular_factor;
2111 specular_color.b *= specular_factor;
2113 if(
material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness) != AI_SUCCESS)
2115 float shininess = 32.0f;
2116 if(
material->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS)
2118 glossiness = math::clamp(std::sqrt((shininess + 2.0f) / 1024.0f), 0.0f, 1.0f);
2122 auto [_, converted_metallic, __] =
2123 convert_specular_gloss_to_metallic_roughness(diffuse_color, specular_color, glossiness);
2124 metallic = converted_metallic;
2126 APPLOG_TRACE(
"Mesh Importer: Converted metallic factor from specular workflow: {:.3f}", metallic);
2131 if (
material->Get(AI_MATKEY_REFLECTIVITY, metallic) != AI_SUCCESS)
2141 if (workflow == material_workflow::specular_gloss)
2144 float glossiness = 0.5f;
2146 if(
material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness) == AI_SUCCESS)
2148 roughness = 1.0f - glossiness;
2149 APPLOG_TRACE(
"Mesh Importer: Converted roughness from glossiness: {:.3f} -> {:.3f}",
2150 glossiness, roughness);
2155 float shininess = 32.0f;
2156 if(
material->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS)
2159 glossiness = math::clamp(std::sqrt((shininess + 2.0f) / 1024.0f), 0.0f, 1.0f);
2160 roughness = 1.0f - glossiness;
2161 APPLOG_TRACE(
"Mesh Importer: Converted roughness from shininess: {:.1f} -> {:.3f}",
2162 shininess, roughness);
2167 aiColor3D diffuse_color = base_color;
2168 aiColor3D specular_color{0.04f, 0.04f, 0.04f};
2169 float specular_factor = 1.0f;
2171 material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse_color);
2172 material->Get(AI_MATKEY_COLOR_SPECULAR, specular_color);
2173 material->Get(AI_MATKEY_SPECULAR_FACTOR, specular_factor);
2175 specular_color.r *= specular_factor;
2176 specular_color.g *= specular_factor;
2177 specular_color.b *= specular_factor;
2179 auto [_, __, converted_roughness] =
2180 convert_specular_gloss_to_metallic_roughness(diffuse_color, specular_color, glossiness);
2181 roughness = converted_roughness;
2183 APPLOG_TRACE(
"Mesh Importer: Converted roughness from full specular workflow: {:.3f}", roughness);
2190 float shininess = 32.0f;
2191 if(
material->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS)
2193 roughness = std::sqrt(2.0f / (shininess + 2.0f));
2194 APPLOG_TRACE(
"Mesh Importer: Converted roughness from legacy shininess: {:.1f} -> {:.3f}",
2195 shininess, roughness);
2205 APPLOG_TRACE(
"Mesh Importer: Final PBR values - BaseColor: ({:.3f}, {:.3f}, {:.3f}), "
2206 "Metallic: {:.3f}, Roughness: {:.3f} [{}{}{}]",
2207 base_color.r, base_color.g, base_color.b, metallic, roughness,
2208 has_base_color ?
"B" :
"b",
2209 has_metallic ?
"M" :
"m",
2210 has_roughness ?
"R" :
"r");
2213void process_material(asset_manager& am,
2214 const fs::path& filename,
2215 const fs::path& output_dir,
2216 const aiScene* scene,
2219 std::vector<imported_texture>& textures)
2227 auto workflow = detect_material_workflow(
material);
2229 APPLOG_TRACE(
"Mesh Importer: Material workflow detected: {}",
2230 workflow == material_workflow::metallic_roughness ?
"Metallic/Roughness" :
2231 workflow == material_workflow::specular_gloss ?
"Specular/Gloss" :
"Unknown");
2235 auto get_imported_texture = [&](
const aiMaterial*
material,
2238 const std::string& semantic,
2239 imported_texture& tex) ->
bool
2242 aiTextureMapping mapping{};
2243 unsigned int uvindex{};
2246 aiTextureMapMode mapmode{};
2247 unsigned int flags{};
2250 aiReturn result = aiGetMaterialTexture(
material,
2264 auto tex_pair = scene->GetEmbeddedTextureAndIndex(path.C_Str());
2266 const auto embedded_texture = tex_pair.first;
2267 if(embedded_texture)
2269 const auto index = tex_pair.second;
2272 tex.name = get_embedded_texture_name(embedded_texture, index, filename, semantic);
2273 tex.embedded_index = index;
2277 tex.name = path.C_Str();
2278 auto texture_filepath = fs::path(tex.name);
2280 auto extension = texture_filepath.extension().string();
2281 auto texture_dir = texture_filepath.parent_path();
2282 auto texture_filename = texture_filepath.filename().stem().string();
2284 if(fixed_name != texture_filename)
2286 auto old_filepath = output_dir / tex.name;
2287 auto fixed_relative = texture_dir / (fixed_name + extension);
2288 auto fixed_filepath = output_dir / fixed_relative;
2291 if(fs::exists(old_filepath, ec))
2293 fs::rename(old_filepath, fixed_filepath, ec);
2298 fs::copy_file(old_filepath, fixed_filepath, ec);
2300 tex.name = fixed_relative.generic_string();
2303 tex.semantic = semantic;
2304 bool use_alpha = flags & aiTextureFlags_UseAlpha;
2305 bool ignore_alpha = flags & aiTextureFlags_IgnoreAlpha;
2306 bool invert = flags & aiTextureFlags_Invert;
2307 tex.inverse = invert;
2311 case aiTextureMapMode_Mirror:
2312 tex.flags = BGFX_SAMPLER_UVW_MIRROR;
2313 case aiTextureMapMode_Clamp:
2314 tex.flags = BGFX_SAMPLER_UVW_CLAMP;
2315 case aiTextureMapMode_Decal:
2316 tex.flags = BGFX_SAMPLER_UVW_BORDER;
2327 auto process_texture = [&](imported_texture& texture, std::vector<imported_texture>& textures,
bool force_process =
false)
2329 if(texture.embedded_index >= 0)
2331 auto it = std::find_if(std::begin(textures),
2333 [&](
const imported_texture& rhs)
2335 return rhs.embedded_index == texture.embedded_index;
2337 if(it != std::end(textures))
2339 texture.name = it->name;
2340 texture.flags = it->flags;
2341 texture.inverse = it->inverse;
2342 texture.process_count = it->process_count;
2347 textures.emplace_back(texture);
2349 if(texture.embedded_index >= 0)
2351 const auto& embedded_texture = scene->mTextures[texture.embedded_index];
2352 process_embedded_texture(embedded_texture, texture.embedded_index, filename, output_dir, textures);
2373 imported_texture texture;
2374 if(get_workflow_aware_texture(
material, workflow,
"BaseColor", texture, get_imported_texture,
false))
2376 process_texture(texture, textures);
2384 aiColor3D base_color_property{1.0f, 1.0f, 1.0f};
2385 float metallic_property = 0.0f;
2386 float roughness_property = 0.5f;
2389 process_material_with_workflow_conversion(
material, workflow,
2390 base_color_property,
2392 roughness_property);
2396 base_color = {base_color_property.r, base_color_property.g, base_color_property.b};
2397 base_color = math::clamp(base_color.value, 0.0f, 1.0f);
2398 mat.set_base_color(base_color);
2401 mat.set_metalness(math::clamp(metallic_property, 0.0f, 1.0f));
2402 mat.set_roughness(math::clamp(roughness_property, 0.0f, 1.0f));
2406 bool uses_duplicate_specular = detect_duplicate_specular_usage(
material, workflow);
2408 if(uses_duplicate_specular)
2411 imported_texture combined_texture;
2412 if(get_imported_texture(
material, aiTextureType_SPECULAR, 0,
"SpecularToMetallicRoughness", combined_texture))
2414 process_texture(combined_texture, textures);
2417 auto texture_asset = am.get_asset<
gfx::texture>(
key.generic_string());
2420 mat.set_metalness_map(texture_asset);
2421 mat.set_roughness_map(texture_asset);
2423 APPLOG_TRACE(
"Mesh Importer: Converting single specular texture to combined metallic/roughness: {}", combined_texture.name);
2432 imported_texture texture;
2433 if(get_workflow_aware_texture(
material, workflow,
"Metallic", texture, get_imported_texture, uses_duplicate_specular))
2435 process_texture(texture, textures);
2438 mat.set_metalness_map(am.get_asset<
gfx::texture>(
key.generic_string()));
2441 if(texture.semantic ==
"SpecularToMetallic")
2443 APPLOG_TRACE(
"Mesh Importer: Converting specular texture to metallic: {}", texture.name);
2450 imported_texture texture;
2451 if(get_workflow_aware_texture(
material, workflow,
"Roughness", texture, get_imported_texture, uses_duplicate_specular))
2453 process_texture(texture, textures);
2456 mat.set_roughness_map(am.get_asset<
gfx::texture>(
key.generic_string()));
2459 if(texture.semantic ==
"GlossToRoughness")
2461 APPLOG_TRACE(
"Mesh Importer: Converting gloss texture to roughness: {}", texture.name);
2463 else if(texture.semantic ==
"SpecularToRoughness")
2465 APPLOG_TRACE(
"Mesh Importer: Converting specular texture to roughness: {}", texture.name);
2480 aiTextureType normals_type = aiTextureType_NORMALS;
2482 static const std::string semantic =
"Normals";
2484 imported_texture texture;
2485 bool has_texture =
false;
2489 has_texture |= get_imported_texture(
material, aiTextureType_NORMALS, 0, semantic, texture);
2494 has_texture |= get_imported_texture(
material, aiTextureType_NORMAL_CAMERA, 0, semantic, texture);
2498 normals_type = aiTextureType_NORMAL_CAMERA;
2504 process_texture(texture, textures);
2513 bool has_property =
false;
2517 has_property |=
material->Get(AI_MATKEY_GLTF_TEXTURE_SCALE(normals_type, 0), property) == AI_SUCCESS;
2522 mat.set_bumpiness(property);
2527 aiTextureType occlusion_type = aiTextureType_AMBIENT_OCCLUSION;
2529 static const std::string semantic =
"Occlusion";
2531 imported_texture texture;
2532 bool has_texture =
false;
2536 has_texture |= get_imported_texture(
material, aiTextureType_AMBIENT_OCCLUSION, 0, semantic, texture);
2541 has_texture |= get_imported_texture(
material, aiTextureType_AMBIENT, 0, semantic, texture);
2545 occlusion_type = aiTextureType_AMBIENT;
2551 has_texture |= get_imported_texture(
material, aiTextureType_LIGHTMAP, 0, semantic, texture);
2554 occlusion_type = aiTextureType_LIGHTMAP;
2560 process_texture(texture, textures);
2570 bool has_property =
false;
2574 has_property |=
material->Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(occlusion_type, 0), property) == AI_SUCCESS;
2584 static const std::string semantic =
"Emissive";
2586 imported_texture texture;
2587 bool has_texture =
false;
2591 has_texture |= get_imported_texture(
material, aiTextureType_EMISSION_COLOR, 0, semantic, texture);
2596 has_texture |= get_imported_texture(
material, aiTextureType_EMISSIVE, 0, semantic, texture);
2601 process_texture(texture, textures);
2604 mat.set_emissive_map(am.get_asset<
gfx::texture>(
key.generic_string()));
2609 aiColor3D
property{};
2610 bool has_property =
false;
2614 has_property |=
material->Get(AI_MATKEY_COLOR_EMISSIVE, property) == AI_SUCCESS;
2620 emissive = {
property.r,
property.g,
property.b};
2621 emissive = math::clamp(emissive.value, 0.0f, 1.0f);
2622 mat.set_emissive_color(emissive);
2627void process_materials(asset_manager& am,
2628 const fs::path& filename,
2629 const fs::path& output_dir,
2630 const aiScene* scene,
2631 std::vector<imported_material>& materials,
2632 std::vector<imported_texture>& textures)
2634 if(scene->mNumMaterials > 0)
2636 materials.resize(scene->mNumMaterials);
2639 for(
size_t i = 0;
i < scene->mNumMaterials; ++
i)
2641 const aiMaterial* assimp_mat = scene->mMaterials[
i];
2643 auto mat = std::make_shared<pbr_material>();
2644 process_material(am, filename, output_dir, scene, assimp_mat, *mat, textures);
2645 std::string assimp_mat_name = assimp_mat->GetName().C_Str();
2646 if(assimp_mat_name.empty())
2648 assimp_mat_name = fmt::format(
"Material {}", filename.string());
2650 materials[
i].mat = mat;
2655void process_embedded_textures(asset_manager& am,
2656 const fs::path& filename,
2657 const fs::path& output_dir,
2658 const aiScene* scene,
2659 std::vector<imported_texture>& textures)
2661 if(scene->mNumTextures > 0)
2663 for(
size_t i = 0;
i < scene->mNumTextures; ++
i)
2665 const aiTexture* assimp_tex = scene->mTextures[
i];
2667 process_embedded_texture(assimp_tex, i, filename, output_dir, textures);
2672void process_imported_scene(asset_manager& am,
2673 const fs::path& filename,
2674 const fs::path& output_dir,
2675 const aiScene* scene,
2676 mesh::load_data& load_data,
2677 std::vector<animation_clip>& animations,
2678 std::vector<imported_material>& materials,
2679 std::vector<imported_texture>& textures)
2681 int meshes_with_bones = 0;
2682 int meshes_without_bones = 0;
2688 auto name_to_index_lut = assign_node_indices(scene);
2690 APPLOG_TRACE(
"Mesh Importer: Processing materials ...");
2691 process_materials(am, filename, output_dir, scene, materials, textures);
2693 APPLOG_TRACE(
"Mesh Importer: Processing embedded textures ...");
2694 process_embedded_textures(am, filename, output_dir, scene, textures);
2697 process_meshes(scene, load_data);
2700 process_nodes(scene, load_data, name_to_index_lut);
2702 APPLOG_TRACE(
"Mesh Importer: Processing animations ...");
2703 process_animations(scene, filename, load_data, name_to_index_lut, animations);
2705 APPLOG_TRACE(
"Mesh Importer: Processing animations bounding boxes ...");
2706 auto boxes = compute_bounding_boxes_for_animations(scene);
2710 load_data.bbox = {};
2711 for(
const auto& kvp : boxes)
2713 for(
const auto&
box : kvp.second)
2715 load_data.bbox.add_point(
box.min);
2716 load_data.bbox.add_point(
box.max);
2720 else if(!load_data.bbox.is_populated())
2722 for(
const auto& submesh : load_data.submeshes)
2724 load_data.bbox.add_point(submesh.bbox.min);
2725 load_data.bbox.add_point(submesh.bbox.max);
2729 APPLOG_TRACE(
"Mesh Importer: bbox min {}, max {}", load_data.bbox.min, load_data.bbox.max);
2732auto read_file(Assimp::Importer& importer,
const fs::path& file, uint32_t flags) ->
const aiScene*
2735 return importer.ReadFile(file.string(), flags);
2742auto convert_specular_gloss_to_metallic_roughness(
const aiColor3D& diffuse_color,
2743 const aiColor3D& specular_color,
2744 float glossiness_factor) -> std::tuple<aiColor3D, float, float>
2749 float max_specular = std::max({specular_color.r, specular_color.g, specular_color.b});
2755 float metallic = 0.0f;
2756 const float dielectric_f0 = 0.04f;
2758 if (max_specular > dielectric_f0)
2761 float specular_above_dielectric = max_specular - dielectric_f0;
2762 float specular_range = 1.0f - dielectric_f0;
2765 metallic = math::clamp(specular_above_dielectric / specular_range, 0.0f, 1.0f);
2768 float avg_specular = (specular_color.r + specular_color.g + specular_color.b) / 3.0f;
2769 float color_variance = std::abs(specular_color.r - avg_specular) +
2770 std::abs(specular_color.g - avg_specular) +
2771 std::abs(specular_color.b - avg_specular);
2774 if (color_variance > 0.1f && avg_specular > 0.3f)
2776 metallic = std::max(metallic, 0.8f);
2783 aiColor3D base_color;
2785 if (metallic > 0.5f)
2789 float specular_influence = 1.0f - max_specular;
2790 base_color.r = specular_color.r + (diffuse_color.r * specular_influence * (1.0f - metallic));
2791 base_color.g = specular_color.g + (diffuse_color.g * specular_influence * (1.0f - metallic));
2792 base_color.b = specular_color.b + (diffuse_color.b * specular_influence * (1.0f - metallic));
2797 base_color = diffuse_color;
2803 float roughness = 1.0f - glossiness_factor;
2806 base_color.r = math::clamp(base_color.r, 0.0f, 1.0f);
2807 base_color.g = math::clamp(base_color.g, 0.0f, 1.0f);
2808 base_color.b = math::clamp(base_color.b, 0.0f, 1.0f);
2809 metallic = math::clamp(metallic, 0.0f, 1.0f);
2810 roughness = math::clamp(roughness, 0.0f, 1.0f);
2812 return std::make_tuple(base_color, metallic, roughness);
2821 struct log_stream :
public Assimp::LogStream
2823 log_stream(Assimp::Logger::ErrorSeverity s) : severity(s)
2827 void write(
const char* message)
override
2831 case Assimp::Logger::Info:
2834 case Assimp::Logger::Warn:
2837 case Assimp::Logger::Err:
2846 Assimp::Logger::ErrorSeverity severity{};
2861 const fs::path& path,
2864 std::vector<animation_clip>& animations,
2865 std::vector<imported_material>& materials,
2866 std::vector<imported_texture>& textures) ->
bool
2868 Assimp::Importer importer;
2870 int rvc_flags = aiComponent_CAMERAS | aiComponent_LIGHTS;
2872 if(!import_meta.model.import_meshes)
2874 rvc_flags |= aiComponent_MESHES;
2877 if(!import_meta.animations.import_animations)
2879 rvc_flags |= aiComponent_ANIMATIONS;
2882 if(!import_meta.materials.import_materials)
2884 rvc_flags |= aiComponent_MATERIALS;
2887 importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, rvc_flags);
2888 importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT);
2889 importer.SetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M,
true);
2890 importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS,
false);
2892 fs::path file = path.stem();
2893 fs::path output_dir = path.parent_path();
2897 uint32_t flags = aiProcess_FlipUVs |
2898 aiProcess_RemoveComponent |
2899 aiProcess_Triangulate |
2900 aiProcess_CalcTangentSpace |
2901 aiProcess_GenUVCoords |
2902 aiProcess_GenSmoothNormals |
2903 aiProcess_GenBoundingBoxes |
2904 aiProcess_ImproveCacheLocality |
2905 aiProcess_LimitBoneWeights |
2906 aiProcess_SortByPType |
2907 aiProcess_TransformUVCoords |
2908 aiProcess_GlobalScale;
2912 if(import_meta.model.weld_vertices)
2914 flags |= aiProcess_JoinIdenticalVertices;
2916 if(import_meta.model.optimize_meshes)
2918 flags |= aiProcess_OptimizeMeshes;
2920 if(import_meta.model.split_large_meshes)
2922 flags |= aiProcess_SplitLargeMeshes;
2924 if(import_meta.model.find_degenerates)
2926 flags |= aiProcess_FindDegenerates;
2928 if(import_meta.model.find_invalid_data)
2930 flags |= aiProcess_FindInvalidData;
2932 if(import_meta.materials.remove_redundant_materials)
2934 flags |= aiProcess_RemoveRedundantMaterials;
2937 APPLOG_TRACE(
"Mesh Importer: Loading {}", path.generic_string());
2939 const aiScene*
scene = read_file(importer, path, flags);
2941 if(
scene ==
nullptr)
2948 aiScene* modScene =
const_cast<aiScene*
>(
scene);
2951 process_imported_scene(am, file, output_dir, modScene, load_data, animations, materials, textures);
2953 APPLOG_TRACE(
"Mesh Importer: Done with {}", path.generic_string());