Skip to content

Commit

Permalink
Merge pull request #308 from carboncopies/306-multithread-asset-impor…
Browse files Browse the repository at this point in the history
…ting

306 multithread asset importing
  • Loading branch information
datacrystals authored Aug 17, 2022
2 parents ad37e1a + c41ff5c commit f6893f6
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ void GUI_Window_ImportModelDirectory::Draw() {
for (const auto &Entry : std::filesystem::recursive_directory_iterator(Path)) {
std::string FilePath{Entry.path().u8string()};
if (EndsWith(FilePath, ".fbx") || EndsWith(FilePath, ".dae") || EndsWith(FilePath, ".obj") || EndsWith(FilePath, ".gltf") || EndsWith(FilePath, ".glb")) {
SystemUtils_->Logger_->Log(std::string("Adding Model '") + FilePath + "' To Import Queue", 4);
SystemUtils_->Logger_->Log(std::string("Adding Model '") + FilePath + "' To Import Queue", 5);
FilePaths.push_back(FilePath);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
struct ERS_STRUCT_ModelWriterData {


const aiScene* ModelScene; /**<Pointer to assimp scene decoded by the importer*/
aiScene* ModelScene; /**<Pointer to assimp scene decoded by the importer*/
Assimp::Importer ModelImporter; /**<This owns the scene, so we keep it around*/
ERS_STRUCT_Model* Model; /**<Pointer to the model to be saved by the writer*/
long ModelAssetID; /**<Asset ID of model geometry*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ void ERS_CLASS_ExternalModelLoader::MergeTextures(ERS_STRUCT_Model* Model, std::
void ERS_CLASS_ExternalModelLoader::ProcessModelTextures(ERS_STRUCT_ModelWriterData &Data) {

// Create List Of Texture Files To Be Copied
std::vector<std::pair<std::string, FIBITMAP*>> ImageBytes;
std::vector<std::pair<std::string, std::future<FIBITMAP*>>> ImageFutures;
for (int i = 0; (long)i < (long)Data.TextureList.size(); i++) {

ERS_STRUCT_IOData IOData;
Expand Down Expand Up @@ -271,21 +271,36 @@ void ERS_CLASS_ExternalModelLoader::ProcessModelTextures(ERS_STRUCT_ModelWriterD
}

if (Success || SecondTryStatus) {
SystemUtils_->Logger_->Log(std::string("Loading Texture Image '") + TexturePath + "'", 4);
SystemUtils_->Logger_->Log(std::string("Starting Thread To Load Texture Image '") + TexturePath + "'", 3);

FIMEMORY* FIImageData = FreeImage_OpenMemory(IOData.Data.get(), IOData.Size_B);
FREE_IMAGE_FORMAT Format = FreeImage_GetFileTypeFromMemory(FIImageData);
FIBITMAP* RawImage = FreeImage_LoadFromMemory(Format, FIImageData);
FreeImage_CloseMemory(FIImageData);

FIBITMAP* Image = FreeImage_ConvertTo32Bits(RawImage);
FreeImage_Unload(RawImage);
SystemUtils_->Logger_->Log(std::string("Loaded Texture Image"), 3);
ImageFutures.push_back(std::make_pair(TexturePath, std::async(std::launch::async,
[&IOData, StartArg = 1]() {
FIMEMORY* FIImageData = FreeImage_OpenMemory(IOData.Data.get(), IOData.Size_B);
FREE_IMAGE_FORMAT Format = FreeImage_GetFileTypeFromMemory(FIImageData);
FIBITMAP* RawImage = FreeImage_LoadFromMemory(Format, FIImageData);
FreeImage_CloseMemory(FIImageData);

FIBITMAP* Image = FreeImage_ConvertTo32Bits(RawImage);
FreeImage_Unload(RawImage);
return Image;
}
)));
SystemUtils_->Logger_->Log(std::string("Created Loader Thread For Image"), 2);

ImageBytes.push_back(std::make_pair(TexturePath, Image));
//ImageFutures.push_back(std::make_pair(TexturePath, Image));
}
}

// Get Futures
std::vector<std::pair<std::string, FIBITMAP*>> ImageBytes;
for (unsigned int i = 0; i < ImageFutures.size(); i++) {
std::string ImagePath = ImageFutures[i].first;
SystemUtils_->Logger_->Log(std::string("Getting Image Bitmap From Thread For Texture '") + ImagePath + "'", 4);
FIBITMAP* Image = ImageFutures[i].second.get();
ImageBytes.push_back(std::make_pair(ImagePath, Image));
}


// Remove Duplicate Stuff (Like Alpha Maps), Just Generally Consolidate Stuff
MergeTextures(Data.Model, &ImageBytes);

Expand Down Expand Up @@ -468,6 +483,29 @@ bool ERS_CLASS_ExternalModelLoader::ReadFile(std::string FilePath, ERS_STRUCT_IO

}

bool ERS_CLASS_ExternalModelLoader::PerformModelSanityChecks(ERS_STRUCT_Model &Model) {

// Check For Meshes
if (Model.Meshes.size() == 0) {
SystemUtils_->Logger_->Log(std::string("Model Has No Meshes, Aborting"), 8);
return false;
}

// Check Meshes For Verts
for (unsigned int i = 0; i < Model.Meshes.size(); i++) {
if (Model.Meshes[i].Vertices.size() == 0) {
SystemUtils_->Logger_->Log(std::string("Warning, Mesh '") + std::to_string(i) + "' Has No Verts", 7);
return false;
}
if (Model.Meshes[i].Indices.size() == 0) {
SystemUtils_->Logger_->Log(std::string("Warning, Mesh '") + std::to_string(i) + "' Has No Indices", 7);
return false;
}
}

// Passed Check
return true;
}

// Load Model From File
bool ERS_CLASS_ExternalModelLoader::LoadModel(std::string ModelPath, ERS_STRUCT_ModelWriterData &Data) {
Expand All @@ -482,18 +520,22 @@ bool ERS_CLASS_ExternalModelLoader::LoadModel(std::string ModelPath, ERS_STRUCT_
const aiScene* Scene = Data.ModelImporter.ReadFile(ModelPath, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_PreTransformVertices | aiProcess_JoinIdenticalVertices);
if (!Scene || Scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !Scene->mRootNode) {
SystemUtils_->Logger_->Log(std::string(std::string("External Model Loading Error: ") + std::string(Data.ModelImporter.GetErrorString())).c_str(), 10);
Data.ModelScene = nullptr;
return false;
}
SystemUtils_->Logger_->Log("Finished Loading External Model, Processing Geometry/Textures", 3);

// Process Geometry, Identify Textures
ProcessNode(Data, Data.Model, Scene->mRootNode, Scene, ModelDirectory);
if (!PerformModelSanityChecks(*Data.Model)) {
return false;
}
DetectBoundingBox(Data.Model);
CalculateTotalVertsIndices(Data.Model);

// Update Struct
Data.ModelOriginDirectoryPath = ModelPath;
Data.ModelScene = Scene;
Data.ModelScene = (aiScene*)Scene;
Data.ModelFileName = ModelFileName;

// Load Textures
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ class ERS_CLASS_ExternalModelLoader {
*/
void MergeTextures(ERS_STRUCT_Model* Model, std::vector<std::pair<std::string, FIBITMAP*>>* LoadedTextures);

/**
* @brief Just makes sure the model has valid sata, etc.
* returns false if there's an issue so we can abort it
*
* @param Model
* @return true
* @return false
*/
bool PerformModelSanityChecks(ERS_STRUCT_Model &Model);

public:


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void ERS_CLASS_ModelImporter::ImportThread() {
}
BlockThread_.unlock();

// Check Queue, Import As Needed
// Check Queue, Import As Needed, Empty Processing Items That Are Done
LockAssetImportQueue_.lock();
if (AssetImportQueue_.size() > 0) {

Expand All @@ -59,11 +59,12 @@ void ERS_CLASS_ModelImporter::ImportThread() {
LockAssetImportQueue_.unlock();


ERS_STRUCT_Model Model;
ERS_STRUCT_ModelWriterData ModelData;
ModelData.Model = &Model;
ModelLoader_->LoadModel(AssetPath, ModelData);
ModelWriter_->WriteModel(ModelData);
ERS_STRUCT_Model Model;
ERS_STRUCT_ModelWriterData ModelData;
ModelData.Model = &Model;
ModelLoader_->LoadModel(AssetPath, ModelData);
ModelWriter_->WriteModel(ModelData);



LockAssetImportQueue_.lock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <string>
#include <vector>
#include <mutex>
#include <future>
#include <thread>

// Third-Party Libraries (BG convention: use <> instead of "")
Expand Down Expand Up @@ -37,6 +38,7 @@ class ERS_CLASS_ModelImporter {
bool HasJobFinished_ = false; /**<Indicate If A Job Has Finished*/
std::thread ImportThread_; /**<Import Processor Thread*/
std::vector<std::string> AssetImportQueue_; /**<List of assets to be imported, accessed by other threads so use mutex to control access*/
std::vector<std::future<void>> ProcessingItems_; /**<List of items currently being worked on by the threads*/

std::unique_ptr<ERS_CLASS_ModelWriter> ModelWriter_; /**<Instance of the model writer, used to save models to the ERS project*/
std::unique_ptr<ERS_CLASS_ExternalModelLoader> ModelLoader_; /**<Used to load models from outside the ERS project*/
Expand Down
47 changes: 37 additions & 10 deletions Source/Core/Writers/ERS_ModelWriter/ERS_ModelWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ bool ERS_CLASS_ModelWriter::WriteModelGeometry(ERS_STRUCT_ModelWriterData &Data,
// Export Model File
Logger_->Log(std::string("Exporting Model Geometry To Blob With Encoding '") + ExportFormat + "'", 4);

if (!Data.ModelScene) {
Logger_->Log("Error Exporting Scene, Scene Is Not Valid", 7);
return false;
}
if (!(bool)Data.ModelScene->HasMeshes()) {
Logger_->Log("Error Exporting Scene, Scene Is Not Valid", 7);
return false;
}
Assimp::Exporter Exporter;
const aiExportDataBlob* Blob = Exporter.ExportToBlob(Data.ModelScene, ExportFormat);

Expand Down Expand Up @@ -247,6 +255,7 @@ void ERS_CLASS_ModelWriter::WriteTextures(ERS_STRUCT_ModelWriterData &Data, std:
}
std::string ERS_CLASS_ModelWriter::GenerateModelMetadata(ERS_STRUCT_ModelWriterData &Data) {


// Generate Metadata
YAML::Emitter MetadataEmitter;
MetadataEmitter<<YAML::BeginMap;
Expand All @@ -267,19 +276,24 @@ std::string ERS_CLASS_ModelWriter::GenerateModelMetadata(ERS_STRUCT_ModelWriterD

// Set Path For Each Texture, Iterate OVer All Levels Of This Texture
std::string TexturePath = Data.TextureNames[i];//TextureList_[i].substr(TextureList_[i].find_last_of("/")+1, TextureList_[i].size()-(TextureList_[i].find_last_of("/")+1));
Logger_->Log(std::string("Saving Information For Texture '") + TexturePath + "'", 3);
MetadataEmitter<<YAML::Key<<TexturePath<<YAML::Value<<YAML::BeginMap;
if (TexturePath.find('*') != std::string::npos) {
Logger_->Log(std::string("Skipping Texture '") + TexturePath + "' Due To Invalid Char", 7);
} else {

for (unsigned int TextureLevel = 0; TextureLevel < Data.TextureMemorySizes[i].size(); TextureLevel++) {
MetadataEmitter<<YAML::Key<<(Data.TextureMemorySizes[i].size() - 1) - TextureLevel<<YAML::Value<<YAML::BeginMap;
Logger_->Log(std::string("Saving Information For Texture '") + TexturePath + "'", 3);
MetadataEmitter<<YAML::Key<<TexturePath<<YAML::Value<<YAML::BeginMap;

MetadataEmitter<<YAML::Key<<"TextureLevelAssetID"<<YAML::Value<<Data.ImageAssetIDs[i][TextureLevel];
MetadataEmitter<<YAML::Key<<"TextureLevelMemorySizeBytes"<<YAML::Value<<Data.TextureMemorySizes[i][TextureLevel];
MetadataEmitter<<YAML::Key<<"TextureLevelResolutionX"<<YAML::Value<<Data.ImageResolutions[i][TextureLevel].first;
MetadataEmitter<<YAML::Key<<"TextureLevelResolutionY"<<YAML::Value<<Data.ImageResolutions[i][TextureLevel].second;
MetadataEmitter<<YAML::Key<<"TextureLevelNumberChannels"<<YAML::Value<<Data.ImageChannels[i][TextureLevel];
for (unsigned int TextureLevel = 0; TextureLevel < Data.TextureMemorySizes[i].size(); TextureLevel++) {
MetadataEmitter<<YAML::Key<<(Data.TextureMemorySizes[i].size() - 1) - TextureLevel<<YAML::Value<<YAML::BeginMap;

MetadataEmitter<<YAML::EndMap;
MetadataEmitter<<YAML::Key<<"TextureLevelAssetID"<<YAML::Value<<Data.ImageAssetIDs[i][TextureLevel];
MetadataEmitter<<YAML::Key<<"TextureLevelMemorySizeBytes"<<YAML::Value<<Data.TextureMemorySizes[i][TextureLevel];
MetadataEmitter<<YAML::Key<<"TextureLevelResolutionX"<<YAML::Value<<Data.ImageResolutions[i][TextureLevel].first;
MetadataEmitter<<YAML::Key<<"TextureLevelResolutionY"<<YAML::Value<<Data.ImageResolutions[i][TextureLevel].second;
MetadataEmitter<<YAML::Key<<"TextureLevelNumberChannels"<<YAML::Value<<Data.ImageChannels[i][TextureLevel];

MetadataEmitter<<YAML::EndMap;
}
}
MetadataEmitter<<YAML::EndMap;
}
Expand Down Expand Up @@ -311,6 +325,19 @@ void ERS_CLASS_ModelWriter::WriteModel(ERS_STRUCT_ModelWriterData &Data) {
// Write
WriteModelGeometry(Data);
WriteTextures(Data, &Data.TextureMemorySizes, &Data.ImageAssetIDs, &Data.ImageResolutions, &Data.ImageChannels);

// Perform List Length Sanity Check
unsigned int CheckSize = Data.TextureList.size();
bool CheckFail = CheckSize != Data.ImageChannels.size();
CheckFail |= CheckSize != Data.ImageAssetIDs.size();
CheckFail |= CheckSize != Data.TextureMemorySizes.size();
CheckFail |= CheckSize != Data.ImageResolutions.size();

if (CheckFail) {
Logger_->Log("Model Failed List Length Sanity Check, Not All Lists Are Of Same Len, Aborting", 8);
return;
}

std::string Metadata = GenerateModelMetadata(Data);

// Write Metadata
Expand Down

0 comments on commit f6893f6

Please sign in to comment.