diff --git a/.github/workflows/public-api-warn.yml b/.github/workflows/public-api-warn.yml index c45ae9d518..d03a6a6c06 100644 --- a/.github/workflows/public-api-warn.yml +++ b/.github/workflows/public-api-warn.yml @@ -3,6 +3,7 @@ on: pull_request_target: paths: - 'library/public/*.h' + - 'library/public/*.h.in' jobs: public-api-warn: diff --git a/application/F3DNSDelegate.mm b/application/F3DNSDelegate.mm index 856f971198..3802d04d81 100644 --- a/application/F3DNSDelegate.mm +++ b/application/F3DNSDelegate.mm @@ -24,7 +24,7 @@ - (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename { (void)theApplication; Starter->AddFile([filename UTF8String]); - Starter->LoadFile(); + Starter->LoadFileGroup(); Starter->Render(); return YES; } diff --git a/application/F3DOptionsTools.cxx b/application/F3DOptionsTools.cxx index 9acccb8643..f41a745d5a 100644 --- a/application/F3DOptionsTools.cxx +++ b/application/F3DOptionsTools.cxx @@ -77,8 +77,7 @@ static inline const std::array CLIOptions = {{ { { "verbose", "", "Set verbose level, providing more information about the loaded data in the console output", "{debug, info, warning, error, quiet}", "debug" }, { "progress", "", "Show loading progress bar", "", "1" }, { "animation-progress", "", "Show animation progress bar", "", "1" }, - { "geometry-only", "", "Do not read materials, cameras and lights from file", "", "1" }, - { "group-geometries", "", "When opening multiple files, show them all in the same scene. Force geometry-only. The configuration file for the first file will be loaded.", "", "1" }, + { "multi-file-mode", "", R"(Choose the behavior when opening multiple files. "single" will show one file at a time, "all" will show all files in a single scene.)", "", "" }, { "up", "", "Up direction", "{-X, +X, -Y, +Y, -Z, +Z}", "" }, { "axis", "x", "Show axes", "", "1" }, { "grid", "g", "Show grid", "", "1" }, { "grid-absolute", "", "Position grid at the absolute origin instead of below the model", "", "1" }, @@ -96,7 +95,7 @@ static inline const std::array CLIOptions = {{ {"animation-frame-rate", "", "Set animation frame rate when playing animation interactively", "", ""}, {"font-file", "", "Path to a FreeType compatible font file", "", ""} } }, { "Material", - { {"point-sprites", "o", "Show sphere sprites instead of geometry", "", "1" }, + { {"point-sprites", "o", "Show sphere sprites instead of surfaces", "", "1" }, {"point-sprites-type", "", "Point sprites type", "", ""}, {"point-sprites-size", "", "Point sprites size", "", ""}, {"point-size", "", "Point size when showing vertices, model specified by default", "", ""}, diff --git a/application/F3DOptionsTools.h b/application/F3DOptionsTools.h index 2c53a227c5..dd89adaa66 100644 --- a/application/F3DOptionsTools.h +++ b/application/F3DOptionsTools.h @@ -36,8 +36,7 @@ static inline const OptionsDict DefaultAppOptions = { { "load-plugins", "" }, { "screenshot-filename", "{app}/{model}_{n}.png" }, { "verbose", "info" }, - { "geometry-only", "false" }, - { "group-geometries", "false" }, + { "multi-file-mode", "single" }, { "resolution", "1000, 600" }, { "position", "" }, { "colormap-file", "" }, diff --git a/application/F3DStarter.cxx b/application/F3DStarter.cxx index 3afb2b5eba..3356ad6740 100644 --- a/application/F3DStarter.cxx +++ b/application/F3DStarter.cxx @@ -74,8 +74,7 @@ class F3DStarter::F3DInternals std::vector Plugins; std::string ScreenshotFilename; std::string VerboseLevel; - bool GeometryOnly; - bool GroupGeometries; + std::string MultiFileMode; std::vector Resolution; std::vector Position; std::string ColorMapFile; @@ -204,9 +203,10 @@ class F3DStarter::F3DInternals dmon_watch_id, dmon_action, const char*, const char* filename, const char*, void* userData) { F3DStarter* self = reinterpret_cast(userData); - const std::lock_guard lock(self->Internals->FilesListMutex); - fs::path filePath = self->Internals->FilesList[self->Internals->CurrentFileIndex]; - if (filePath.filename().string() == std::string(filename)) + const std::lock_guard lock(self->Internals->LoadedFilesMutex); + if (std::find_if(self->Internals->LoadedFiles.begin(), self->Internals->LoadedFiles.end(), + [&](const auto& path) + { return path.filename() == filename; }) != self->Internals->LoadedFiles.end()) { self->Internals->ReloadFileRequested = true; } @@ -254,6 +254,24 @@ class F3DStarter::F3DInternals const std::regex numberingRe("\\{(n:?([0-9]*))\\}"); const std::regex dateRe("date:?([A-Za-z%]*)"); + /* Return a file related string depending on the currently loaded files, or the empty string if + * a single file is loaded */ + const auto fileCheck = [&]() + { + if (this->LoadedFiles.empty()) + { + return "no_file"; + } + else if (this->LoadedFiles.size() > 1) + { + return "multi_file"; + } + else + { + return ""; + } + }; + /* return value for template variable name (eg. `app` -> `F3D`) */ const auto variableLookup = [&](const std::string& var) { @@ -271,15 +289,30 @@ class F3DStarter::F3DInternals } else if (var == "model") { - return FilesList[CurrentFileIndex].stem().string(); + std::string output = fileCheck(); + if (output.empty()) + { + output = this->LoadedFiles[0].stem().string(); + } + return output; } else if (var == "model.ext") { - return FilesList[CurrentFileIndex].filename().string(); + std::string output = fileCheck(); + if (output.empty()) + { + output = this->LoadedFiles[0].filename().string(); + } + return output; } else if (var == "model_ext") { - return FilesList[CurrentFileIndex].extension().string().substr(1); + std::string output = fileCheck(); + if (output.empty()) + { + output = this->LoadedFiles[0].extension().string().substr(1); + } + return output; } else if (std::regex_match(var, dateRe)) { @@ -404,9 +437,11 @@ class F3DStarter::F3DInternals std::to_string(maxNumberingAttempts) + " attempts"); } - void UpdateOptions( - const std::vector& entriesVector, const std::string& inputFile) + void UpdateOptions(const std::vector& entriesVector, + const std::vector& paths) { + assert(!paths.empty()); + f3d::log::debug("Updating Options:"); // Initialize libf3dOptions f3d::options libOptions; @@ -419,67 +454,73 @@ class F3DStarter::F3DInternals bool logOptions = this->AppOptions.VerboseLevel == "debug"; std::map> loggingMap; - // For each config entries, ordered by priority - for (const auto& entries : entriesVector) + // For each input file, order matter + for (const auto& tmpPath : paths) { - // For each entry (eg: difference config files) - for (auto const& [conf, source, pattern] : entries) + std::string inputFile = tmpPath.string(); + // For each config entries, ordered by priority + for (const auto& entries : entriesVector) { - std::regex re(pattern, std::regex_constants::icase); - std::smatch matches; - // If the source is empty, there is no pattern, all options applies - // Note: An empty inputFile matches with ".*" - if (source.empty() || std::regex_match(inputFile, matches, re)) + // For each entry (eg: difference config files) + for (auto const& [conf, source, pattern] : entries) { - // For each option key/value - for (auto const& [key, value] : conf) + std::regex re(pattern, std::regex_constants::icase); + std::smatch matches; + // If the source is empty, there is no pattern, all options applies + // Note: An empty inputFile matches with ".*" + if (source.empty() || std::regex_match(inputFile, matches, re)) { - // Check in appOptions first - auto appIter = appOptions.find(key); - if (appIter != appOptions.end()) + // For each option key/value + for (auto const& [key, value] : conf) { - appOptions[key] = value; - if (logOptions) + // Check in appOptions first + auto appIter = appOptions.find(key); + if (appIter != appOptions.end()) { - loggingMap.emplace(key, std::tuple(key, source, pattern, value)); + appOptions[key] = value; + if (logOptions) + { + loggingMap.emplace(key, std::tuple(key, source, pattern, value)); + } + continue; } - continue; - } - std::string libf3dOptionName = key; + std::string libf3dOptionName = key; - // Convert key into a libf3d option name if possible - auto libf3dIter = F3DOptionsTools::LibOptionsNames.find(key); - if (libf3dIter != F3DOptionsTools::LibOptionsNames.end()) - { - libf3dOptionName = std::string(libf3dIter->second); - } + // Convert key into a libf3d option name if possible + auto libf3dIter = F3DOptionsTools::LibOptionsNames.find(key); + if (libf3dIter != F3DOptionsTools::LibOptionsNames.end()) + { + libf3dOptionName = std::string(libf3dIter->second); + } - try - { - // Assume this is a libf3d option and set the value - libOptions.setAsString(libf3dOptionName, value); - } - catch (const f3d::options::parsing_exception& ex) - { - std::string origin = - source.empty() ? pattern : source.string() + ":`" + pattern + "`"; - f3d::log::warn("Could not set '", key, "' to '", value, "' from ", origin, - " because: ", ex.what()); - continue; - } - catch (const f3d::options::inexistent_exception&) - { - std::string origin = - source.empty() ? pattern : source.string() + ":`" + pattern + "`"; - auto [closestName, dist] = F3DOptionsTools::GetClosestOption(libf3dOptionName, true); - f3d::log::warn("'", key, "' option from ", origin, - " does not exists , did you mean '", closestName, "'?"); - continue; - } - if (logOptions) - { - loggingMap.emplace(libf3dOptionName, std::tuple(key, source, pattern, value)); + try + { + // Assume this is a libf3d option and set the value + libOptions.setAsString(libf3dOptionName, value); + + // Log the option if needed + if (logOptions) + { + loggingMap.emplace(libf3dOptionName, std::tuple(key, source, pattern, value)); + } + } + catch (const f3d::options::parsing_exception& ex) + { + std::string origin = + source.empty() ? pattern : source.string() + ":`" + pattern + "`"; + f3d::log::warn("Could not set '", key, "' to '", value, "' from ", origin, + " because: ", ex.what()); + } + catch (const f3d::options::inexistent_exception&) + { + std::string origin = + source.empty() ? pattern : source.string() + ":`" + pattern + "`"; + auto [closestName, dist] = + F3DOptionsTools::GetClosestOption(libf3dOptionName, true); + f3d::log::warn("'", key, "' option from ", origin, + " does not exists , did you mean '", closestName, "'?"); + } } } } @@ -492,6 +533,7 @@ class F3DStarter::F3DInternals std::string origin = source.empty() ? pattern : source.string() + ":`" + pattern + "`"; f3d::log::debug(" '", name, "' = '", value, "' from ", origin); } + f3d::log::debug(""); // Update typed app options from the string version this->UpdateTypedAppOptions(appOptions); @@ -522,8 +564,8 @@ class F3DStarter::F3DInternals this->AppOptions.ScreenshotFilename = f3d::options::parse(appOptions.at("screenshot-filename")); this->AppOptions.VerboseLevel = f3d::options::parse(appOptions.at("verbose")); - this->AppOptions.GeometryOnly = f3d::options::parse(appOptions.at("geometry-only")); - this->AppOptions.GroupGeometries = f3d::options::parse(appOptions.at("group-geometries")); + this->AppOptions.MultiFileMode = + f3d::options::parse(appOptions.at("multi-file-mode")); this->AppOptions.Resolution = f3d::options::parse>(appOptions.at("resolution")); this->AppOptions.Position = f3d::options::parse>(appOptions.at("position")); @@ -577,7 +619,7 @@ class F3DStarter::F3DInternals { window.setSize(this->AppOptions.Resolution[0], this->AppOptions.Resolution[1]); } - else if (this->AppOptions.Resolution.size() != 0) + else if (!this->AppOptions.Resolution.empty()) { f3d::log::warn("Provided resolution could not be applied"); } @@ -588,7 +630,7 @@ class F3DStarter::F3DInternals } else { - if (this->AppOptions.Position.size() != 0) + if (!this->AppOptions.Position.empty()) { f3d::log::warn("Provided position could not be applied"); } @@ -603,19 +645,34 @@ class F3DStarter::F3DInternals } } + // Recover a vector of unique parent paths from a vector of paths + static std::vector ParentPaths(const std::vector& paths) + { + std::vector parents; + for (const auto& tmpPath : paths) + { + fs::path parentPath = tmpPath.parent_path(); + if (std::find(parents.begin(), parents.end(), tmpPath) == parents.end()) + { + parents.emplace_back(parentPath); + } + } + return parents; + } + F3DAppOptions AppOptions; f3d::options LibOptions; F3DOptionsTools::OptionsEntries ConfigOptionsEntries; F3DOptionsTools::OptionsEntries CLIOptionsEntries; F3DOptionsTools::OptionsEntries DynamicOptionsEntries; std::unique_ptr Engine; - std::vector FilesList; - dmon_watch_id FolderWatchId; - bool LoadedFile = false; + std::vector> FilesGroups; + std::vector LoadedFiles; + std::vector FolderWatchIds; + int CurrentFilesGroupIndex = -1; // dmon used atomic and mutex - std::atomic CurrentFileIndex = -1; - std::mutex FilesListMutex; + std::mutex LoadedFilesMutex; // Event loop atomics std::atomic RenderRequested = false; @@ -688,7 +745,7 @@ int F3DStarter::Start(int argc, char** argv) // Update app and libf3d options based on config entries, with an empty input file // config < cli this->Internals->UpdateOptions( - { this->Internals->ConfigOptionsEntries, this->Internals->CLIOptionsEntries }, ""); + { this->Internals->ConfigOptionsEntries, this->Internals->CLIOptionsEntries }, { "" }); #if __APPLE__ // Initialize MacOS delegate @@ -720,25 +777,25 @@ int F3DStarter::Start(int argc, char** argv) { if (keySym == "Left") { - return this->LoadRelativeFile(-1); + return this->LoadRelativeFileGroup(-1); } if (keySym == "Right") { - return this->LoadRelativeFile(+1); + return this->LoadRelativeFileGroup(+1); } if (keySym == "Up") { - return this->LoadRelativeFile(0, true); + return this->LoadRelativeFileGroup(0, true, true); } if (keySym == "Down") { - if (this->Internals->LoadedFile) + if (this->Internals->LoadedFiles.size() > 0) { - this->AddFile( - this->Internals->FilesList[static_cast(this->Internals->CurrentFileIndex)] - .parent_path(), - true); - return this->LoadRelativeFile(0); + for (const auto& parentPath : F3DInternals::ParentPaths(this->Internals->LoadedFiles)) + { + this->AddFile(parentPath, true); + } + return this->LoadRelativeFileGroup(0); } return true; } @@ -783,7 +840,7 @@ int F3DStarter::Start(int argc, char** argv) } if (index > -1) { - this->LoadFile(index); + this->LoadFileGroup(index); } this->RequestRender(); return true; @@ -800,7 +857,7 @@ int F3DStarter::Start(int argc, char** argv) } // Load a file - this->LoadFile(); + this->LoadFileGroup(); if (!this->Internals->AppOptions.NoRender) { @@ -836,7 +893,7 @@ int F3DStarter::Start(int argc, char** argv) // Render and compare with file if needed if (!reference.empty()) { - if (!this->Internals->LoadedFile && !noDataForceRender) + if (this->Internals->LoadedFiles.empty() && !noDataForceRender) { f3d::log::error("No file loaded, no rendering performed"); return EXIT_FAILURE; @@ -886,7 +943,7 @@ int F3DStarter::Start(int argc, char** argv) f3d::log::info("Image comparison success with an error difference of: ", error); } - if (this->Internals->FilesList.size() > 1 && !this->Internals->AppOptions.GroupGeometries) + if (this->Internals->FilesGroups.size() > 1) { f3d::log::warn("Image comparison was performed using a single 3D file, other provided " "3D files were ignored."); @@ -895,9 +952,9 @@ int F3DStarter::Start(int argc, char** argv) // Render to file if needed else if (!output.empty()) { - if (!this->Internals->LoadedFile && !noDataForceRender) + if (this->Internals->LoadedFiles.empty() && !noDataForceRender) { - f3d::log::error("No file loaded, no rendering performed"); + f3d::log::error("No files loaded, no rendering performed"); return EXIT_FAILURE; } @@ -917,7 +974,7 @@ int F3DStarter::Start(int argc, char** argv) f3d::log::debug("Output image saved to ", path); } - if (this->Internals->FilesList.size() > 1 && !this->Internals->AppOptions.GroupGeometries) + if (this->Internals->FilesGroups.size() > 1) { f3d::log::warn("An output image was saved using a single 3D file, other provided 3D " "files were ignored."); @@ -941,7 +998,52 @@ int F3DStarter::Start(int argc, char** argv) } //---------------------------------------------------------------------------- -void F3DStarter::LoadFile(int index, bool relativeIndex) +void F3DStarter::LoadFileGroup(int index, bool relativeIndex, bool forceClear) +{ + int groupIndex = this->Internals->CurrentFilesGroupIndex; + if (relativeIndex) + { + groupIndex += index; + } + else + { + groupIndex = index; + } + + // Compute a modulo to ensure 0 < groupIndex < size + // XXX Do not work if groupIndex + size < 0 + int size = static_cast(this->Internals->FilesGroups.size()); + if (size != 0) + { + groupIndex = (groupIndex + size) % size; + } + else + { + groupIndex = -1; + } + + if (groupIndex >= 0) + { + // Clear only if we change the group to load + bool clear = forceClear ? true : this->Internals->CurrentFilesGroupIndex != groupIndex; + this->Internals->CurrentFilesGroupIndex = groupIndex; + + // Create a nice looking group index eg: "(1/5)" + // XXX: Each group contains at least one path + std::string groupIdx = "(" + std::to_string(groupIndex + 1) + "/" + + std::to_string(this->Internals->FilesGroups.size()) + ")"; + this->LoadFileGroup(this->Internals->FilesGroups[groupIndex], clear, groupIdx); + } + else + { + this->Internals->CurrentFilesGroupIndex = groupIndex; + this->LoadFileGroup(std::vector{}, true, ""); + } +} + +//---------------------------------------------------------------------------- +void F3DStarter::LoadFileGroup( + const std::vector& paths, bool clear, const std::string& groupIdx) { // Make sure the animation is stopped before trying to load any file if (!this->Internals->AppOptions.NoRender) @@ -949,7 +1051,7 @@ void F3DStarter::LoadFile(int index, bool relativeIndex) this->Internals->Engine->getInteractor().stopAnimation(); } - f3d::log::debug("========== Loading 3D file =========="); + f3d::log::debug("========== Loading 3D files =========="); // Recover current options from the engine const f3d::options& dynamicOptions = this->Internals->Engine->getOptions(); @@ -976,159 +1078,155 @@ void F3DStarter::LoadFile(int index, bool relativeIndex) // Recover file information f3d::loader& loader = this->Internals->Engine->getLoader(); - fs::path filePath; - std::string filenameInfo; - size_t size = this->Internals->FilesList.size(); - if (size != 0) - { - if (relativeIndex) - { - assert(this->Internals->CurrentFileIndex >= 0); - this->Internals->CurrentFileIndex += index; - } - else - { - this->Internals->CurrentFileIndex = index; - } - // Create a nice looking filename info eg: "cow.vtp (1/5)" - // XXX Do not work if CurrentFileIndex + size < 0 - size_t fileIndex = (this->Internals->CurrentFileIndex + size) % size; - filePath = this->Internals->FilesList[fileIndex]; - filenameInfo = "(" + std::to_string(fileIndex + 1) + "/" + - std::to_string(this->Internals->FilesList.size()) + ") " + filePath.filename().string(); - - this->Internals->CurrentFileIndex = static_cast(fileIndex); - } - else - { - f3d::log::debug("No file to load provided."); - this->Internals->CurrentFileIndex = -1; - } + bool unsupported = false; - if (this->Internals->CurrentFileIndex >= 0) + std::vector localPaths; + try { - if (this->Internals->AppOptions.GroupGeometries) + // In the main thread, we only need to guard writing + const std::lock_guard lock(this->Internals->LoadedFilesMutex); + + if (clear) { - // Group geometries mode, consider the first file configuration file only - this->Internals->CurrentFileIndex = 0; - filePath = this->Internals->FilesList[static_cast(this->Internals->CurrentFileIndex)]; + loader.clear(); + this->Internals->LoadedFiles.clear(); } - // Update app and libf3d options based on config entries, selecting block using the input file - // config < cli < dynamic - this->Internals->UpdateOptions( - { this->Internals->ConfigOptionsEntries, this->Internals->CLIOptionsEntries, - this->Internals->DynamicOptionsEntries }, - filePath.string()); - this->Internals->Engine->setOptions(this->Internals->LibOptions); - this->Internals->ApplyPositionAndResolution(); - - // Load any new plugins the updated app options - F3DPluginsTools::LoadPlugins(this->Internals->AppOptions.Plugins); - - // Position the loaded file flag before we start loading - this->Internals->LoadedFile = false; - - // Check the size of the file before loading it - // XXX: Not considered in the context of GroupGeometries - static constexpr int BYTES_IN_MIB = 1048576; - if (this->Internals->AppOptions.MaxSize >= 0.0 && - fs::file_size(filePath) > - static_cast(this->Internals->AppOptions.MaxSize * BYTES_IN_MIB)) + if (paths.empty()) { - f3d::log::info("No file loaded, file is bigger than max size"); + f3d::log::debug("No files to load provided"); } else { - try + // Update app and libf3d options based on config entries, selecting block using the input file + // config < cli < dynamic + // Options must be updated before checking the supported files in order to load plugins + std::vector configPaths = this->Internals->LoadedFiles; + std::copy(paths.begin(), paths.end(), std::back_inserter(configPaths)); + this->Internals->UpdateOptions( + { this->Internals->ConfigOptionsEntries, this->Internals->CLIOptionsEntries, + this->Internals->DynamicOptionsEntries }, + configPaths); + + this->Internals->Engine->setOptions(this->Internals->LibOptions); + this->Internals->ApplyPositionAndResolution(); + + f3d::log::debug("Checking files:"); + for (const fs::path& tmpPath : paths) { - if (loader.hasSceneReader(filePath.string()) && !this->Internals->AppOptions.GeometryOnly && - !this->Internals->AppOptions.GroupGeometries) - { - loader.loadScene(filePath.string()); - this->Internals->LoadedFile = true; - } - else if (loader.hasGeometryReader(filePath.string())) + if (std::find(this->Internals->LoadedFiles.begin(), this->Internals->LoadedFiles.end(), + tmpPath) == this->Internals->LoadedFiles.end()) { - // In GroupGeometries, just load all the files from the list - if (this->Internals->AppOptions.GroupGeometries) + if (loader.supports(tmpPath)) { - int nGeom = 0; - for (size_t i = 0; i < size; i++) + // Check the size of the file before loading it + static constexpr int BYTES_IN_MIB = 1048576; + if (this->Internals->AppOptions.MaxSize >= 0.0 && + fs::file_size(tmpPath) > + static_cast(this->Internals->AppOptions.MaxSize * BYTES_IN_MIB)) { - auto geomPath = this->Internals->FilesList[i]; - if (loader.hasGeometryReader(geomPath.string())) - { - // Reset for the first file, then add geometries without resetting - // XXX this means the scene is reset and loaded from scratch every time a file is - // dropped This could be improved - loader.loadGeometry(this->Internals->FilesList[i].string(), i == 0 ? true : false); - nGeom++; - } - else - { - f3d::log::warn(geomPath, " is not a geometry of a supported file format\n"); - } + f3d::log::info(tmpPath.string(), " skipped, file is bigger than max size"); } - if (nGeom > 1) + else { - filenameInfo = std::to_string(nGeom) + " geometries loaded"; + localPaths.emplace_back(tmpPath); } } else { - // Standard loadGeometry code - loader.loadGeometry(filePath.string(), true); + f3d::log::warn(tmpPath.string(), " is not a file of a supported file format"); + unsupported = true; } - this->Internals->LoadedFile = true; } - else - { - f3d::log::debug("No reader found for \"" + filePath.string() + "\""); - f3d::log::warn(filePath.string(), " is not a file of a supported file format\n"); - filenameInfo += " [UNSUPPORTED]"; - } - } - catch (const f3d::loader::load_failure_exception& ex) - { - // XXX Not reachable until vtkImporter is improved to support returning a failure - f3d::log::error("Could not load file: ", ex.what()); } - if (!dynamicOptions.scene.camera.index.has_value() && !this->Internals->AppOptions.NoRender) + if (!localPaths.empty()) { - // Setup the camera according to options - this->Internals->SetupCamera(this->Internals->AppOptions.CamConf); + // Add files to the scene + loader.add(localPaths); - this->Internals->Engine->getWindow().setWindowName( - filePath.filename().string() + " - " + F3D::AppName); + // Update loaded files + std::copy( + localPaths.begin(), localPaths.end(), std::back_inserter(this->Internals->LoadedFiles)); } } } + catch (const f3d::loader::load_failure_exception& ex) + { + f3d::log::error("Some of these files could not be loaded: ", ex.what()); + for (const fs::path& tmpPath : localPaths) + { + f3d::log::error(" ", tmpPath.string()); + } + unsupported = true; + } + + if (!dynamicOptions.scene.camera.index.has_value() && !this->Internals->AppOptions.NoRender) + { + // Setup the camera according to options + this->Internals->SetupCamera(this->Internals->AppOptions.CamConf); + } - if (this->Internals->LoadedFile) + std::string filenameInfo; + if (this->Internals->LoadedFiles.size() > 0) { + // Loaded files, create a filename info like this: + // "(1/5) cow.vtp + N [+UNSUPPORTED]" + filenameInfo = groupIdx + " " + this->Internals->LoadedFiles.at(0).filename().string(); + if (this->Internals->LoadedFiles.size() > 1) + { + filenameInfo += " +" + std::to_string(this->Internals->LoadedFiles.size() - 1); + } + if (unsupported) + { + filenameInfo += " [+UNSUPPORTED]"; + } + + // Update dmon watch logic if (this->Internals->AppOptions.Watch) { // Always unwatch and watch current folder, even on reload - if (this->Internals->FolderWatchId.id > 0) + for (const auto& dmonId : this->Internals->FolderWatchIds) { - dmon_unwatch(this->Internals->FolderWatchId); + if (dmonId.id > 0) + { + dmon_unwatch(dmonId); + } + } + this->Internals->FolderWatchIds.clear(); + + for (const auto& parentPath : F3DInternals::ParentPaths(this->Internals->LoadedFiles)) + { + this->Internals->FolderWatchIds.emplace_back( + dmon_watch(parentPath.string().c_str(), &F3DInternals::dmonFolderChanged, 0, this)); } - this->Internals->FolderWatchId = dmon_watch( - filePath.parent_path().string().c_str(), &F3DInternals::dmonFolderChanged, 0, this); } } else { - // No file loaded, remove any previously loaded file - loader.loadGeometry("", true); + // No files loaded, create a simple filename info like this: + // (1/5) cow.vtt [UNSUPPORTED] + // (1/1) cow.vtt [+UNSUPPORTED] + if (unsupported) + { + filenameInfo = groupIdx + " " + paths.at(0).filename().string() + " ["; + if (paths.size() > 1) + { + filenameInfo += "+"; + } + filenameInfo += "UNSUPPORTED]"; + } + } + + if (!this->Internals->AppOptions.NoRender) + { + this->Internals->Engine->getWindow().setWindowName(filenameInfo + " - " + F3D::AppName); } // XXX: We can force dropzone and filename_info because they cannot be set // manually by the user for now f3d::options& options = this->Internals->Engine->getOptions(); - options.ui.dropzone = !this->Internals->LoadedFile; + options.ui.dropzone = this->Internals->LoadedFiles.empty(); options.ui.filename_info = filenameInfo; } @@ -1208,6 +1306,7 @@ void F3DStarter::SaveScreenshot(const std::string& filenameTemplate, bool minima //---------------------------------------------------------------------------- int F3DStarter::AddFile(const fs::path& path, bool quiet) { + // Check file exists auto tmpPath = fs::absolute(path); if (!fs::exists(tmpPath)) { @@ -1217,6 +1316,7 @@ int F3DStarter::AddFile(const fs::path& path, bool quiet) } return -1; } + // If file is a folder, add files recursively else if (fs::is_directory(tmpPath)) { std::set sortedPaths; @@ -1229,45 +1329,73 @@ int F3DStarter::AddFile(const fs::path& path, bool quiet) // Recursively add all files this->AddFile(entryPath, quiet); } - return static_cast(this->Internals->FilesList.size()) - 1; + return static_cast(this->Internals->FilesGroups.size()) - 1; } else { - auto it = - std::find(this->Internals->FilesList.begin(), this->Internals->FilesList.end(), tmpPath); + // Check if file has already been added + bool found = false; + std::vector>::iterator it; + for (it = this->Internals->FilesGroups.begin(); it != this->Internals->FilesGroups.end(); it++) + { + auto localIt = std::find(it->begin(), it->end(), tmpPath); + found |= localIt != it->end(); + if (found) + { + break; + } + } - if (it == this->Internals->FilesList.end()) + if (!found) { - // In the main thread, we only need to guard writing - const std::lock_guard lock(this->Internals->FilesListMutex); - this->Internals->FilesList.push_back(tmpPath); - return static_cast(this->Internals->FilesList.size()) - 1; + // Add to the right file group + // XXX more multi-file mode may be added in the future + if (this->Internals->AppOptions.MultiFileMode == "all") + { + if (this->Internals->FilesGroups.empty()) + { + this->Internals->FilesGroups.resize(1); + } + assert(this->Internals->FilesGroups.size() == 1); + this->Internals->FilesGroups[0].emplace_back(tmpPath); + } + else + { + if (this->Internals->AppOptions.MultiFileMode != "single") + { + f3d::log::warn("Unrecognized multi-file-mode: ", + this->Internals->AppOptions.MultiFileMode, ". Assuming \"single\" mode."); + } + this->Internals->FilesGroups.emplace_back(std::vector{ tmpPath }); + } + return static_cast(this->Internals->FilesGroups.size()) - 1; } else { + // If already added, just return the index of the group containing the file if (!quiet) { f3d::log::warn("File ", tmpPath.string(), " has already been added"); } - return static_cast(std::distance(this->Internals->FilesList.begin(), it)); + return static_cast(std::distance(this->Internals->FilesGroups.begin(), it)); } } } //---------------------------------------------------------------------------- -bool F3DStarter::LoadRelativeFile(int index, bool restoreCamera) +bool F3DStarter::LoadRelativeFileGroup(int index, bool restoreCamera, bool forceClear) { if (restoreCamera) { f3d::camera& cam = this->Internals->Engine->getWindow().getCamera(); const auto camState = cam.getState(); - this->LoadFile(index, true); + this->LoadFileGroup(index, true, forceClear); cam.setState(camState); } else { - this->LoadFile(index, true); + this->LoadFileGroup(index, true, forceClear); } this->RequestRender(); @@ -1280,7 +1408,7 @@ void F3DStarter::EventLoop() { if (this->Internals->ReloadFileRequested) { - this->LoadRelativeFile(0, true); + this->LoadRelativeFileGroup(0, true, true); this->Internals->ReloadFileRequested = false; } if (this->Internals->RenderRequested) diff --git a/application/F3DStarter.h b/application/F3DStarter.h index 578f3946dd..f175cf6120 100644 --- a/application/F3DStarter.h +++ b/application/F3DStarter.h @@ -21,17 +21,17 @@ class F3DStarter int Start(int argc, char** argv); /** - * Add a file or directory to the list of paths - * Returns the index of the added file + * Add a file or directory to a file group + * Returns the index of the group where the file was added */ int AddFile(const std::filesystem::path& path, bool quiet = false); /** - * Load a file if any have been added - * Set the index to select the index of the file to load + * Load a file group if any have been added + * Set the index to select the index of the file group to load * Set relativeIndex to true to use the index as a relative index with the current index */ - void LoadFile(int index = 0, bool relativeIndex = false); + void LoadFileGroup(int index = 0, bool relativeIndex = false, bool forceClear = false); /** * Trigger a render on the next event loop @@ -63,9 +63,18 @@ class F3DStarter /** * Internal method triggered when interacting with the application - * that load a file using relative index and handle camera restore + * that load a file group using relative index and handle camera restore */ - bool LoadRelativeFile(int relativeIndex = 0, bool restoreCamera = false); + bool LoadRelativeFileGroup( + int relativeIndex = 0, bool restoreCamera = false, bool forceClear = false); + + /** + * Internal method used to load a provided file group into the scene. + * Set clear to true to clear the scene first + * GroupIdx is only used for display purposes of the filename + */ + void LoadFileGroup( + const std::vector& paths, bool clear, const std::string& groupIdx); /** * Internal event loop that is triggered repeatedly to handle specific events: diff --git a/application/testing/CMakeLists.txt b/application/testing/CMakeLists.txt index f8550621ce..5625bfc938 100644 --- a/application/testing/CMakeLists.txt +++ b/application/testing/CMakeLists.txt @@ -1,7 +1,7 @@ # F3D Testing function(f3d_test) - cmake_parse_arguments(F3D_TEST "TONE_MAPPING;LONG_TIMEOUT;INTERACTION;INTERACTION_CONFIGURE;NO_BASELINE;NO_RENDER;NO_OUTPUT;WILL_FAIL;NO_DATA_FORCE_RENDER" "NAME;CONFIG;DATA;RESOLUTION;THRESHOLD;REGEXP;REGEXP_FAIL;HDRI" "DEPENDS;ARGS" ${ARGN}) + cmake_parse_arguments(F3D_TEST "TONE_MAPPING;LONG_TIMEOUT;INTERACTION;INTERACTION_CONFIGURE;NO_BASELINE;NO_RENDER;NO_OUTPUT;WILL_FAIL;NO_DATA_FORCE_RENDER" "NAME;CONFIG;RESOLUTION;THRESHOLD;REGEXP;REGEXP_FAIL;HDRI" "DATA;DEPENDS;ARGS" ${ARGN}) if(F3D_TEST_CONFIG) list(APPEND F3D_TEST_ARGS "--config=${F3D_TEST_CONFIG}") @@ -10,7 +10,9 @@ function(f3d_test) endif() if (F3D_TEST_DATA) - list(APPEND F3D_TEST_ARGS "${F3D_SOURCE_DIR}/testing/data/${F3D_TEST_DATA}") + foreach(_single_data ${F3D_TEST_DATA}) + list(APPEND F3D_TEST_ARGS "${F3D_SOURCE_DIR}/testing/data/${_single_data}") + endforeach() endif() if(F3D_TEST_INTERACTION) @@ -117,7 +119,7 @@ function(f3d_test) endfunction() f3d_test(NAME TestPLY DATA suzanne.ply) -f3d_test(NAME TestOBJ DATA suzanne.obj ARGS --geometry-only) +f3d_test(NAME TestOBJ DATA world.obj) f3d_test(NAME TestSTL DATA suzanne.stl) f3d_test(NAME TestVTU DATA dragon.vtu) f3d_test(NAME TestVTP DATA cow.vtp) @@ -150,7 +152,6 @@ f3d_test(NAME TestScalarsWithBar DATA suzanne.ply ARGS -b -s --coloring-array=No f3d_test(NAME TestGLTFImporter DATA f3d.glb) f3d_test(NAME TestGLTFImporterWithAnimation DATA BoxAnimated.gltf ARGS --animation-time=2 --animation-progress) f3d_test(NAME TestGLTFSkin DATA SimpleSkin.gltf) -f3d_test(NAME TestGLTFReaderWithAnimation DATA BoxAnimated.gltf ARGS --geometry-only --animation-time=2 --animation-progress) f3d_test(NAME TestDicom DATA IM-0001-1983.dcm ARGS --scalar-coloring --roughness=1) f3d_test(NAME TestMHD DATA HeadMRVolume.mhd ARGS --scalar-coloring --roughness=1) f3d_test(NAME TestVTICell DATA waveletMaterial.vti ARGS -s --coloring-array=Material -c --roughness=1) @@ -176,13 +177,14 @@ f3d_test(NAME TestVolumeComp DATA vase_4comp.vti ARGS -vb --comp=3 LONG_TIMEOUT) f3d_test(NAME TestVolumeDirect DATA vase_4comp.vti ARGS -vb --comp=-2 LONG_TIMEOUT) f3d_test(NAME TestVolumeCells DATA waveletArrays.vti ARGS -vb --cells LONG_TIMEOUT) f3d_test(NAME TestVolumeNonScalars DATA waveletArrays.vti ARGS -vb -s --coloring-array=RandomPointScalars LONG_TIMEOUT) -f3d_test(NAME TestTextureNormal DATA WaterBottle.glb ARGS --geometry-only --texture-normal=${F3D_SOURCE_DIR}/testing/data/normal.png --normal-scale=0.1) -f3d_test(NAME TestTextureMaterial DATA WaterBottle.glb ARGS --geometry-only --texture-material=${F3D_SOURCE_DIR}/testing/data/red_mod.jpg --roughness=1 --metallic=1) -f3d_test(NAME TestTextureMaterialWithOptions DATA WaterBottle.glb ARGS --geometry-only --texture-material=${F3D_SOURCE_DIR}/testing/data/red_mod.jpg --roughness=0.5 --metallic=0.5) -f3d_test(NAME TestTextureEmissive DATA WaterBottle.glb ARGS --geometry-only --texture-emissive=${F3D_SOURCE_DIR}/testing/data/red.jpg --emissive-factor=0.1,0.1,0.1) -f3d_test(NAME TestTextures DATA WaterBottle.glb ARGS --geometry-only --texture-material=${F3D_SOURCE_DIR}/testing/data/red.jpg --roughness=1 --metallic=1 --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo.png --texture-normal=${F3D_SOURCE_DIR}/testing/data/normal.png --texture-emissive=${F3D_SOURCE_DIR}/testing/data/red.jpg --emissive-factor=0.1,0.1,0.1) +f3d_test(NAME TestTextureNormal DATA WaterBottle.glb ARGS --texture-normal=${F3D_SOURCE_DIR}/testing/data/normal.png --normal-scale=0.1) +f3d_test(NAME TestTextureMaterial DATA WaterBottle.glb ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/red_mod.jpg --roughness=1 --metallic=1) +f3d_test(NAME TestTextureMaterialWithOptions DATA WaterBottle.glb ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/red_mod.jpg --roughness=0.5 --metallic=0.5) +f3d_test(NAME TestTextureEmissive DATA WaterBottle.glb ARGS --texture-emissive=${F3D_SOURCE_DIR}/testing/data/red.jpg --emissive-factor=0.1,0.1,0.1) +f3d_test(NAME TestTextures DATA WaterBottle.glb ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/red.jpg --roughness=1 --metallic=1 --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo.png --texture-normal=${F3D_SOURCE_DIR}/testing/data/normal.png --texture-emissive=${F3D_SOURCE_DIR}/testing/data/red.jpg --emissive-factor=0.1,0.1,0.1) f3d_test(NAME TestMetaDataImporter DATA BoxAnimated.gltf ARGS -m) f3d_test(NAME TestMultiblockMetaData DATA mb.vtm ARGS -m) +f3d_test(NAME TestMultiFileMetaData DATA mb/recursive ARGS -m --multi-file-mode=all) f3d_test(NAME TestTIFF DATA f3d.tif ARGS -sy --up=-Y) f3d_test(NAME TestLightIntensityBrighter DATA cow.vtp ARGS --light-intensity=5.0) f3d_test(NAME TestLightIntensityDarker DATA cow.vtp ARGS --light-intensity=0.2) @@ -192,25 +194,29 @@ f3d_test(NAME TestUTF8 DATA "(ノಠ益ಠ )ノ.vtp") f3d_test(NAME TestFilenameCommasSpaces DATA "tetrahedron, with commas & spaces.stl") f3d_test(NAME TestFont DATA suzanne.ply ARGS -n --font-file=${F3D_SOURCE_DIR}/testing/data/Crosterian.ttf) f3d_test(NAME TestAnimationIndex DATA InterpolationTest.glb ARGS --animation-index=7 --animation-time=0.5 --animation-progress) +f3d_test(NAME TestMultiFileAnimationIndex DATA InterpolationTest.glb BoxAnimated.gltf ARGS --animation-index=9 --animation-time=0.85 --animation-progress --multi-file-mode=all) f3d_test(NAME TestAnimationAutoplay DATA InterpolationTest.glb ARGS --animation-autoplay) f3d_test(NAME TestMaxSizeBelow DATA suzanne.stl ARGS --max-size=1) -f3d_test(NAME TestMaxSizeAbove DATA WaterBottle.glb ARGS --max-size=0.2 REGEXP "No file loaded, file is bigger than max size" NO_BASELINE) -f3d_test(NAME TestAlternativeOptionSyntax DATA WaterBottle.glb ARGS --max-size 0.2 REGEXP "No file loaded, file is bigger than max size" NO_BASELINE) +f3d_test(NAME TestMaxSizeAbove DATA WaterBottle.glb ARGS --max-size=0.2 REGEXP "file is bigger than max size" NO_BASELINE) +f3d_test(NAME TestMaxSizeAboveMultiFile DATA suzanne.obj WaterBottle.glb ARGS --multi-file-mode=all --max-size=0.6 --translucency-support --opacity=0.5) +f3d_test(NAME TestAlternativeOptionSyntax DATA WaterBottle.glb ARGS --max-size 0.2 REGEXP "file is bigger than max size" NO_BASELINE) f3d_test(NAME TestNonExistentFile DATA nonExistentFile.vtp ARGS --filename WILL_FAIL) f3d_test(NAME TestUnsupportedFile DATA unsupportedFile.dummy ARGS --filename WILL_FAIL) f3d_test(NAME TestComponentName DATA from_abq.vtu ARGS --scalar-coloring --bar --comp=2) f3d_test(NAME TestNoRender DATA dragon.vtu NO_RENDER) f3d_test(NAME TestNoRenderWithOptions DATA dragon.vtu ARGS --hdri-ambient --axis NO_RENDER) # These options causes issues if not handled correctly f3d_test(NAME TestNoFile NO_DATA_FORCE_RENDER) -f3d_test(NAME TestGroupGeometries DATA mb/recursive ARGS --group-geometries) -f3d_test(NAME TestGroupGeometriesColoring DATA mb/recursive ARGS --group-geometries -s --coloring-array=Polynomial -b) -f3d_test(NAME TestMultiInputPositionals ARGS ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --group-geometries -s --coloring-array=Polynomial -b) -f3d_test(NAME TestMultiInputArg ARGS --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --group-geometries -s --coloring-array=Polynomial -b) -f3d_test(NAME TestMultiInputMultiArgs ARGS --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --group-geometries -s --coloring-array=Polynomial -b) +f3d_test(NAME TestMultiFile DATA mb/recursive ARGS --multi-file-mode=all) +f3d_test(NAME TestMultiFileColoring DATA mb/recursive ARGS --multi-file-mode=all -s --coloring-array=Polynomial -b) +f3d_test(NAME TestMultiFileVolume DATA multi ARGS --multi-file-mode=all -vsb --coloring-array=Scalars_) +f3d_test(NAME TestMultiFileColoringTexture DATA mb/recursive/mb_1_0.vtp mb/recursive/mb_2_0.vtp world.obj ARGS --multi-file-mode=all -sb --coloring-array=Normals --comp=1) +f3d_test(NAME TestMultiFilePositionals DATA mb/recursive/mb_0_0.vtu mb/recursive/mb_1_0.vtp ARGS --multi-file-mode=all -s --coloring-array=Polynomial -b) +f3d_test(NAME TestMultiInputArg ARGS --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --multi-file-mode=all -s --coloring-array=Polynomial -b) +f3d_test(NAME TestMultiInputMultiArgs ARGS --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --multi-file-mode=all -s --coloring-array=Polynomial -b) f3d_test(NAME TestInvalidUpDirection DATA suzanne.ply ARGS -g --up=W REGEXP "W is not a valid up direction" NO_BASELINE) f3d_test(NAME TestUpDirectionNoSign DATA suzanne.ply ARGS --up=X) f3d_test(NAME TestTextureMatCap DATA suzanne.ply ARGS --texture-matcap=${F3D_SOURCE_DIR}/testing/data/skin.png) -f3d_test(NAME TestTextureMatCapWithTCoords DATA WaterBottle.glb ARGS --geometry-only --texture-matcap=${F3D_SOURCE_DIR}/testing/data/skin.png) +f3d_test(NAME TestTextureMatCapWithTCoords DATA WaterBottle.glb ARGS --texture-matcap=${F3D_SOURCE_DIR}/testing/data/skin.png) f3d_test(NAME TestConfigOrder DATA suzanne.ply ARGS CONFIG ${F3D_SOURCE_DIR}/testing/configs/config_order.json) # `.+` > `.*` alphabetically but overridden by the order f3d_test(NAME TestOutputStream DATA suzanne.ply ARGS --verbose=quiet --output=- REGEXP "^.PNG" NO_BASELINE NO_OUTPUT) f3d_test(NAME TestOutputStreamInfo DATA suzanne.ply ARGS --verbose=info --output=- REGEXP "redirected to stderr" NO_BASELINE NO_OUTPUT) @@ -268,20 +274,20 @@ f3d_ss_test(NAME UserModelN TEMPLATE {model}_{n}.png EXPECTED ${_screenshot_user set_tests_properties(f3d::TestScreenshotUserModelN PROPERTIES ENVIRONMENT "XDG_PICTURES_DIR=${_screenshot_user_dir};HOME=${_screenshot_user_dir};USERPROFILE=${_screenshot_user_dir}") if(NOT APPLE OR VTK_VERSION VERSION_GREATER_EQUAL 9.3.0) - f3d_test(NAME TestTextureColor DATA WaterBottle.glb ARGS --geometry-only --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo_mod.png --translucency-support) + f3d_test(NAME TestTextureColor DATA WaterBottle.glb ARGS --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo_mod.png --translucency-support) endif() # Needs SSBO: https://gitlab.kitware.com/vtk/vtk/-/merge_requests/10675 if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20231108) if(APPLE) # MacOS does not support OpenGL 4.3 - f3d_test(NAME TestSkinningManyBonesFailure DATA tube_254bones.glb ARGS -v REGEXP "which requires OpenGL" NO_BASELINE) + f3d_test(NAME TestSkinningManyBonesFailure DATA tube_254bones.glb ARGS --verbose REGEXP "which requires OpenGL" NO_BASELINE) else() # Strictly speaking, this test can also fail if ran without OpenGL 4.3 support on Windows and Linux # Instead of checking MacOS only, we should try to get OpenGL capabilities from CMake later instead f3d_test(NAME TestSkinningManyBones DATA tube_254bones.glb) endif() else() - f3d_test(NAME TestSkinningManyBonesWarning DATA tube_254bones.glb ARGS -v REGEXP "with more than 250 bones \\\(254\\\)" NO_BASELINE) + f3d_test(NAME TestSkinningManyBonesWarning DATA tube_254bones.glb ARGS --verbose REGEXP "with more than 250 bones \\\(254\\\)" NO_BASELINE) endif() # Needs splat sorting with compute shaders @@ -313,6 +319,8 @@ if(NOT F3D_MACOS_BUNDLE) f3d_test(NAME TestConfigFileBuild DATA dragon.vtu CONFIG complex_build.json) f3d_test(NAME TestConfigStemBuild DATA dragon.vtu CONFIG complex_build) f3d_test(NAME TestConfigFileUpperCase DATA suzanne_upper.STL CONFIG complex_build) + f3d_test(NAME TestConfigFileMultiFileSTL DATA mb/recursive/mb_1_0.vtp suzanne.stl ARGS --multi-file-mode=all CONFIG complex_build) + f3d_test(NAME TestConfigFileMultiFileVTP DATA mb/recursive/mb_1_0.vtp suzanne.stl mb/recursive/mb_2_0.vtp ARGS --multi-file-mode=all CONFIG complex_build) file(COPY "${F3D_SOURCE_DIR}/resources/configs/config.d/" "${F3D_SOURCE_DIR}/plugins/native/configs/config.d/" DESTINATION "${CMAKE_BINARY_DIR}/share/f3d/configs/config_build.d") f3d_test(NAME TestDefaultConfigFileVTU DATA dragon.vtu CONFIG config_build LONG_TIMEOUT TONE_MAPPING) @@ -332,12 +340,12 @@ endif() # color texture with opacity needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/9467 if(VTK_VERSION VERSION_GREATER_EQUAL 9.2.20220811) - f3d_test(NAME TestTextureColorWithOptions DATA WaterBottle.glb ARGS --geometry-only --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo_mod.png --color=1,1,0 --opacity=0.4 --translucency-support) + f3d_test(NAME TestTextureColorWithOptions DATA WaterBottle.glb ARGS --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo_mod.png --color=1,1,0 --opacity=0.4 --translucency-support) endif() -f3d_test(NAME TestOBJImporter DATA world.obj) f3d_test(NAME TestGLTFImporterUnlit DATA UnlitTest.glb) f3d_test(NAME TestMaterial DATA suzanne.ply ARGS --color=0.72,0.45,0.2 --metallic=0.7 --roughness=0.2) +f3d_test(NAME TestMaterialFullScene DATA WaterBottle.glb ARGS --color=0.9,0.1,0.1 --metallic=0.7 --roughness=0.2) f3d_test(NAME TestMetaData DATA pdiag.vtu ARGS -m) f3d_test(NAME TestEdges DATA suzanne.ply ARGS -e) f3d_test(NAME TestLineWidth DATA cow.vtk ARGS -e --line-width=5) @@ -387,6 +395,16 @@ endif() f3d_test(NAME TestCameraPersp DATA Cameras.gltf ARGS --camera-index=0) f3d_test(NAME TestCameraOrtho DATA Cameras.gltf ARGS --camera-index=1) f3d_test(NAME TestCameraIndexConfiguration DATA Cameras.gltf ARGS --camera-index=0 --camera-azimuth-angle=15 --camera-position=0.7,0.5,3) +f3d_test(NAME TestCameraIndexInvalid DATA Cameras.gltf ARGS --camera-index=3 REGEXP "is higher than the number of available camera" NO_BASELINE) +f3d_test(NAME TestCameraIndexNegative DATA Cameras.gltf ARGS --camera-index=-1 REGEXP "Invalid camera index" NO_BASELINE) + +# Require improved importer support https://gitlab.kitware.com/vtk/vtk/-/merge_requests/11303 +if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240910) + f3d_test(NAME TestInvalidFileFileName DATA invalid.vtp ARGS --filename NO_DATA_FORCE_RENDER) + f3d_test(NAME TestMultiFileInvalidFilesFileName DATA mb/mb_3_0.vtt invalid.vtp ARGS --multi-file-mode=all --filename NO_DATA_FORCE_RENDER) + f3d_test(NAME TestMultiFileCameraIndex DATA Cameras.gltf CameraAnimated.glb ARGS --multi-file-mode=all --camera-index=2 --opacity=0.5 --translucency-support) +endif() + # Test Verbose camera f3d_test(NAME TestVerboseCamera DATA Cameras.gltf ARGS --camera-index=1 --verbose NO_RENDER REGEXP "0:.*1:") @@ -574,8 +592,7 @@ if(F3D_PLUGIN_BUILD_DRACO) # Needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/10884 if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240214) f3d_test(NAME TestGLTFDracoImporter DATA Box_draco.gltf ARGS --load-plugins=draco --camera-position=-1.6,1.3,2.7) - f3d_test(NAME TestGLTFDracoReader DATA Box_draco.gltf ARGS --load-plugins=draco --geometry-only --camera-position=-1.6,1.3,2.7) - f3d_test(NAME TestGLTFDracoReaderWithoutCompression DATA BoxAnimated.gltf ARGS --load-plugins=draco --geometry-only --animation-time=2 --animation-progress) + f3d_test(NAME TestGLTFDracoImporterWithoutCompression DATA BoxAnimated.gltf ARGS --load-plugins=draco --animation-time=2 --animation-progress) endif() if(NOT F3D_MACOS_BUNDLE) @@ -647,13 +664,15 @@ if(F3D_PLUGIN_BUILD_OCCT) f3d_test(NAME TestBinaryBREP DATA f3d.bin.brep ARGS --load-plugins=occt --up=+Z) if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240707) - f3d_test(NAME TestInvalidBREP DATA invalid.brep ARGS --verbose --load-plugins=occt REGEXP "failed to load geometry" NO_BASELINE) + f3d_test(NAME TestInvalidBREP DATA invalid.brep ARGS --verbose --load-plugins=occt REGEXP "failed to load scene" NO_BASELINE) endif() if(F3D_PLUGIN_OCCT_COLORING_SUPPORT) f3d_test(NAME TestXCAFColors DATA xcaf-colors.stp ARGS --load-plugins=occt -csy --up=+Z --line-width=3 --camera-direction=-1,-1,-1) f3d_test(NAME TestXCAFColorsXBF DATA xcaf-colors.xbf ARGS --load-plugins=occt -csy --up=+Z --line-width=3 --camera-direction=-1,-1,-1) - f3d_test(NAME TestInvalidXBF DATA invalid.xbf ARGS --verbose --load-plugins=occt REGEXP "A reader failed to update" NO_BASELINE) + if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240707) + f3d_test(NAME TestInvalidXBF DATA invalid.xbf ARGS --verbose --load-plugins=occt REGEXP "failed to load scene" NO_BASELINE) + endif() if (NOT F3D_MACOS_BUNDLE) file(COPY "${F3D_SOURCE_DIR}/plugins/occt/configs/config.d/" DESTINATION "${CMAKE_BINARY_DIR}/share/f3d/configs/config_build.d") @@ -723,10 +742,12 @@ f3d_test(NAME TestInteractionMisc DATA cow.vtp NO_BASELINE INTERACTION) #KK f3d_test(NAME TestInteractionCycleCell DATA waveletArrays.vti INTERACTION) #VCCC f3d_test(NAME TestInteractionCycleComp DATA dragon.vtu INTERACTION) #SYYYY f3d_test(NAME TestInteractionCycleScalars DATA dragon.vtu INTERACTION) #BSSSS +f3d_test(NAME TestInteractionCycleCellInvalidIndex DATA waveletArrays.vti INTERACTION) #SSC f3d_test(NAME TestInteractionVolumeInverse DATA HeadMRVolume.mhd ARGS --camera-position=127.5,-400,127.5 --camera-view-up=0,0,1 INTERACTION) #VI +f3d_test(NAME TestInteractionMultiFileVolume DATA multi ARGS --multi-file-mode=all INTERACTION) #SSVB f3d_test(NAME TestInteractionPointCloud DATA pointsCloud.vtp ARGS --point-sprites-size=20 INTERACTION) #O f3d_test(NAME TestInteractionDirectory DATA mb INTERACTION ARGS --scalar-coloring) #Right;Right;Right;Left;Up; -f3d_test(NAME TestInteractionDirectoryLoop DATA mb INTERACTION ARGS --scalar-coloring) #Left;Left;Left; +f3d_test(NAME TestInteractionDirectoryLoop DATA mb/recursive INTERACTION ARGS --scalar-coloring --filename) #Left;Left;Left;Left;Left; f3d_test(NAME TestInteractionDirectoryEmpty DATA mb INTERACTION NO_DATA_FORCE_RENDER) #Right;Right;Right; f3d_test(NAME TestInteractionDirectoryEmptyVerbose DATA mb ARGS --verbose NO_BASELINE INTERACTION REGEXP "is not a file of a supported file format") #Right;Right;Right;HMCSY f3d_test(NAME TestInteractionAnimationNotStopped DATA InterpolationTest.glb NO_BASELINE INTERACTION)#Space; @@ -744,11 +765,11 @@ f3d_test(NAME TestInteractionFocalPointPickingDefault DATA dragon.vtu INTERACTIO f3d_test(NAME TestInteractionFocalPointPickingShift DATA dragon.vtu INTERACTION) f3d_test(NAME TestInteractionFocalPointPickingPoints DATA pointsCloud.vtp INTERACTION) f3d_test(NAME TestInteractionLightIntensity DATA dragon.vtu INTERACTION) -f3d_test(NAME TestInteractionGroupGeometriesColoring DATA mb/recursive ARGS --group-geometries INTERACTION) #SSB +f3d_test(NAME TestInteractionMultiFileColoring DATA mb/recursive ARGS --multi-file-mode=all INTERACTION) #SSSB f3d_test(NAME TestInteractionReload DATA dragon.vtu ARGS -e INTERACTION) #Up; f3d_test(NAME TestInteractionLoadParentDirectory DATA multi/dragon.vtu ARGS --filename INTERACTION) #Down; -f3d_test(NAME TestInteractionEmptyLoadParentDirectory INTERACTION NO_BASELINE REGEXP "No file loaded, no rendering performed") #Down; -f3d_test(NAME TestInteractionGroupGeometriesLoadParentDirectory DATA mb/mb_0_0.vtu ARGS --group-geometries --filename INTERACTION) #Down; +f3d_test(NAME TestInteractionEmptyLoadParentDirectory INTERACTION NO_BASELINE REGEXP "No files loaded, no rendering performed") #Down; +f3d_test(NAME TestInteractionMultiFileLoadParentDirectory DATA mb/mb_0_0.vtu ARGS --multi-file-mode=all --filename INTERACTION) #Down; f3d_test(NAME TestInteractionInvertZoom DATA suzanne.ply ARGS --invert-zoom INTERACTION) f3d_test(NAME TestInteractionCameraHotkeys DATA cow.vtp INTERACTION) f3d_test(NAME TestInteractionZoomToMouse DATA cow.vtp INTERACTION) @@ -769,7 +790,7 @@ endif() f3d_test(NAME TestInteractionCycleAnimationNoAnimation DATA cow.vtp INTERACTION NO_BASELINE) #W f3d_test(NAME TestInteractionDropFiles INTERACTION_CONFIGURE)#X;DropEvent cow.vtp;DropEvent dragon.vtu suzanne.stl; -f3d_test(NAME TestInteractionGroupGeometriesDrop DATA mb/mb_1_0.vtp ARGS --group-geometries -e INTERACTION_CONFIGURE) #DropEvent mb_2_0.vtp +f3d_test(NAME TestInteractionMultiFileDrop DATA mb/mb_1_0.vtp ARGS --multi-file-mode=all -e INTERACTION_CONFIGURE) #DropEvent mb_2_0.vtp f3d_test(NAME TestInteractionDropSameFiles ARGS -x INTERACTION_CONFIGURE)#DropEvent cow.vtp;#DropEvent dragon.vtu;#DropEvent cow.vtp#DropEvent cow.vtp; # HDRI test needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/9767 @@ -792,7 +813,7 @@ endif() # Output option test f3d_test(NAME TestOutput DATA cow.vtp NO_BASELINE) f3d_test(NAME TestOutputOutput DATA cow.vtp ARGS --ref=${CMAKE_BINARY_DIR}/Testing/Temporary/TestOutput.png DEPENDS TestOutput NO_BASELINE) -f3d_test(NAME TestUnsupportedInputOutput DATA unsupportedFile.dummy REGEXP "No file loaded, no rendering performed" NO_BASELINE) +f3d_test(NAME TestUnsupportedInputOutput DATA unsupportedFile.dummy REGEXP "No files loaded, no rendering performed" NO_BASELINE) f3d_test(NAME TestOutputNoBackground DATA cow.vtp ARGS --no-background NO_BASELINE) # Test Non existent interaction record directory @@ -834,11 +855,14 @@ f3d_test(NAME TestVerboseDefaultScalar DATA HeadMRVolume.mhd ARGS -s --verbose R # Incorrect component test f3d_test(NAME TestIncorrectComponent DATA dragon.vtu ARGS -s --comp=4 REGEXP "Invalid component index: 4" NO_BASELINE) +# Incorrect volume coloring with multi file +f3d_test(NAME TestIncorrectMultiFileVolume DATA multi ARGS -sv --coloring-array=Normals --multi-file-mode=all REGEXP "Cannot find the array \"Normals\" to display volume with" NO_BASELINE) + # Incorrect color map f3d_test(NAME TestIncorrectColormap DATA IM-0001-1983.dcm ARGS --scalar-coloring --roughness=1 --colormap=0,1,0,0,1,0,1 REGEXP "Specified color map list count is not a multiple of 4, ignoring it." NO_BASELINE) # Test opening a directory -f3d_test(NAME TestVerboseDirectory DATA mb REGEXP "Loading 3D geometry: .*mb_._0.vt." NO_RENDER) +f3d_test(NAME TestVerboseDirectory DATA mb REGEXP "mb_0_0.vtu" NO_RENDER) # Test opening multiple file and rendering only one f3d_test(NAME TestVerboseMultiFileRender DATA mb REGEXP "An output image was saved using a single 3D file, other provided 3D files were ignored." NO_BASELINE) @@ -864,7 +888,7 @@ f3d_test(NAME TestTensorsDirect DATA tensors.vti ARGS -s --coloring-array=tensor f3d_test(NAME TestTensorsVolumeDirect DATA tensors.vti ARGS -v -s --coloring-array=tensors1 --comp=-2 REGEXP "Direct scalars rendering not supported by array with more than 4 components" NO_BASELINE) # Test volume rendering without any array -f3d_test(NAME TestVerboseVolumeNoArray DATA cow.vtp ARGS -v REGEXP "Cannot use volume with this dataset" NO_BASELINE) +f3d_test(NAME TestVerboseVolumeNoArray DATA cow.vtp ARGS -v REGEXP "Cannot use volume with this data" NO_BASELINE) # Test scalar rendering without any array f3d_test(NAME TestVerboseNoArray DATA cow.vtp ARGS -s --verbose=debug REGEXP "No array to color with" NO_BASELINE) @@ -895,19 +919,22 @@ f3d_test(NAME TestUnsupportedFileText DATA unsupportedFile.dummy ARGS --filename f3d_test(NAME TestNonExistentTexture DATA cow.vtp ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/dummy.png REGEXP "Texture file does not exist" NO_BASELINE) if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240707) - # Test invalid geometry - f3d_test(NAME TestInvalidGeometry DATA world_invalid.obj ARGS --geometry-only REGEXP "failed to load geometry" NO_BASELINE) - - # Test invalid full scene file - f3d_test(NAME TestInvalidFullScene DATA duck_invalid.gltf REGEXP "failed to load scene" NO_BASELINE) + # Test invalid file + f3d_test(NAME TestInvalidFile DATA duck_invalid.gltf REGEXP "failed to load scene" NO_BASELINE) - # Test invalid geometry with animation - f3d_test(NAME TestInteractionAnimationInvalid DATA BoxAnimated_invalid_animation.gltf ARGS --geometry-only REGEXP "A reader failed to update at a timeValue" INTERACTION NO_BASELINE) + # Test invalid animation + f3d_test(NAME TestAnimationInvalid DATA BoxAnimated_invalid_animation.gltf ARGS --animation-time 1 REGEXP "Could not load time value: 1" NO_BASELINE) endif () # Test invalid texture f3d_test(NAME TestInvalidTexture DATA cow.vtp ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/invalid.png REGEXP "Cannot open texture file" NO_BASELINE) +# Test invalid color +f3d_test(NAME TestInvalidColor DATA cow.vtp ARGS --color=0,0,0,1 REGEXP "Invalid surface color provided, not applying" NO_BASELINE) + +# Test invalid emmisive factor +f3d_test(NAME TestInvalidEmissiveFactor DATA cow.vtp ARGS --emissive-factor=0,0,0,1 REGEXP "Invalid emissive factor provided, not applying" NO_BASELINE) + # Test non existent interaction file, do not add a TestNonExistentInteraction f3d_test(NAME TestNonExistentInteraction DATA cow.vtp INTERACTION REGEXP "Interaction record file to play does not exist" NO_BASELINE) @@ -927,6 +954,13 @@ f3d_test(NAME TestNonExistentConfigFileStem DATA cow.vtp CONFIG "dummy" REGEXP " # Test invalid config file f3d_test(NAME TestInvalidConfigFile DATA cow.vtp CONFIG ${F3D_SOURCE_DIR}/testing/configs/invalid.json REGEXP "Unable to parse the configuration file" NO_BASELINE) +# Test invalid multifile mode +f3d_test(NAME TestInvalidMultiFileMode DATA mb/recursive ARGS --multi-file-mode=add REGEXP "Unrecognized multi-file-mode: add. Assuming \"single\" mode." NO_BASELINE) + +# Test unnamed cameras/animation +f3d_test(NAME TestVerboseUnnamedCamera DATA Cameras.gltf ARGS --verbose REGEXP "1: unnamed_1" NO_BASELINE) +f3d_test(NAME TestVerboseUnnamedAnimation DATA BoxAnimated.gltf ARGS --verbose REGEXP "0: unnamed_0" NO_BASELINE) + # Test invalid value in config file f3d_test(NAME TestConfigFileInvalidValue DATA cow.vtp CONFIG ${F3D_SOURCE_DIR}/testing/configs/invalid_value.json REGEXP "must be a string, a boolean or a number" NO_BASELINE) @@ -940,7 +974,7 @@ f3d_test(NAME TestConfigFileInexistentKey DATA cow.vtp CONFIG ${F3D_SOURCE_DIR}/ f3d_test(NAME TestConfigFileQuiet DATA nonExistentFile.vtp CONFIG ${F3D_SOURCE_DIR}/testing/configs/quiet.json REGEXP_FAIL "File .*/testing/data/nonExistentFile.vtp does not exist" NO_BASELINE) # Test no file with config file -f3d_test(NAME TestNoFileConfigFile CONFIG ${F3D_SOURCE_DIR}/testing/configs/verbose.json ARGS --verbose REGEXP "No file to load provided." NO_BASELINE) +f3d_test(NAME TestNoFileConfigFile CONFIG ${F3D_SOURCE_DIR}/testing/configs/verbose.json ARGS --verbose REGEXP "No files to load provided" NO_BASELINE) # Test help display f3d_test(NAME TestHelp ARGS --help REGEXP "Usage:") @@ -968,6 +1002,15 @@ set_tests_properties(f3d::TestInvalidCLIArgs PROPERTIES PASS_REGULAR_EXPRESSION # Test that f3d resolution can be controlled from config file add_test(NAME f3d::TestConfigResolution COMMAND $ --config=${F3D_SOURCE_DIR}/testing/configs/resolution.json ${F3D_SOURCE_DIR}/testing/data/suzanne.stl --output=${CMAKE_BINARY_DIR}/Testing/Temporary/TestConfigResolution.png --ref=${F3D_SOURCE_DIR}/testing/baselines/TestConfigResolution.png) +# Test filename template with multiple files +add_test(NAME f3d::TestMultiFileFileNameTemplate COMMAND $ ${F3D_SOURCE_DIR}/testing/data/suzanne.stl ${F3D_SOURCE_DIR}/testing/data/dragon.vtu --output=${CMAKE_BINARY_DIR}/Testing/Temporary/{model.ext}.png --multi-file-mode=all --verbose) +set_tests_properties(f3d::TestMultiFileFileNameTemplate PROPERTIES PASS_REGULAR_EXPRESSION "Output image saved to \".*/Testing/Temporary/multi_file.png\"") + +# Test filename template with no files +add_test(NAME f3d::TestNoFileFileNameTemplate COMMAND $ --output=${CMAKE_BINARY_DIR}/Testing/Temporary/{model.ext}.png --verbose) +set_tests_properties(f3d::TestNoFileFileNameTemplate PROPERTIES PASS_REGULAR_EXPRESSION "Output image saved to \".*/Testing/Temporary/no_file.png\"") +set_tests_properties(f3d::TestNoFileFileNameTemplate PROPERTIES ENVIRONMENT "CTEST_F3D_NO_DATA_FORCE_RENDER=1") + # Test failure without a reference, please do not create a TestNoRef.png file f3d_test(NAME TestNoRef DATA cow.vtp WILL_FAIL) diff --git a/doc/libf3d/BINDINGS.md b/doc/libf3d/BINDINGS.md index a695d16eab..26ab28a89e 100644 --- a/doc/libf3d/BINDINGS.md +++ b/doc/libf3d/BINDINGS.md @@ -17,7 +17,7 @@ eng.options.update({ "render.grid.enable": True, }) -eng.loader.load_geometry("f3d/testing/data/dragon.vtu") +eng.loader.add("f3d/testing/data/dragon.vtu") eng.interactor.start() ``` @@ -41,7 +41,7 @@ public class F3DExample { // Always use try-with-resources idiom to ensure the native engine is released try (Engine engine = new Engine(Window.Type.NATIVE)) { Loader loader = engine.getLoader(); - loader.loadGeometry("f3d/testing/data/dragon.vtu"); + loader.add("f3d/testing/data/dragon.vtu"); engine.getWindow().render(); } diff --git a/doc/libf3d/CLASSES.md b/doc/libf3d/CLASSES.md index 10b5b7f404..1cb39517df 100644 --- a/doc/libf3d/CLASSES.md +++ b/doc/libf3d/CLASSES.md @@ -15,7 +15,8 @@ All static plugins can be loaded using `f3d::engine::autoloadPlugins()`. ## Loader class -The loader class is responsible to read and load the file from the disk. It supports reading full scene files as well as multiple geometries into a default scene. +The loader class is responsible to `add` file from the disk into the scene. It supports reading multiple files at the same time and even mesh from memory. +It is possible to `clear` the scene and to check if a file is `supported`. ## Window class @@ -55,7 +56,7 @@ A class to control logging in the libf3d. Simple using the different dedicated m ## Options class -This class lets you control the behavior of the libf3d. An option is basically a string used as a key associated with a value, see the exhaustive [list](OPTIONS.md). +This class lets you control the behavior of the libf3d. An option is basically a value that can be a optional or not. There is different API to access it, see the exhaustive [doc](OPTIONS.md). ## Reader class diff --git a/doc/libf3d/OPTIONS.md b/doc/libf3d/OPTIONS.md index 10aba01ae4..41753ad1ee 100644 --- a/doc/libf3d/OPTIONS.md +++ b/doc/libf3d/OPTIONS.md @@ -9,8 +9,8 @@ The possible option are listed below and are organized by categories and subcate * `render` options are related to the way the render is done * `render.effect` options are related to specific techniques used that modify the render * `ui` options are related to the screenspace UI element displayed - * `model` options are related to modifications on the model, they are only meaningful when using the default scene - * `interactor` options requires an interactor to be present to have any effect. + * `model` options are related to modifications on the model + * `interactor` options requires an interactor to be present to have any effect Please note certain options are taken into account when rendering, others when loading a file. See the exhaustive list below, but note that this may change in the future. @@ -27,8 +27,8 @@ scene.animation.index|int
0
load|Select the animation to load.
Any nega scene.animation.speed_factor|double
1
render|Set the animation speed factor to slow, speed up or even invert animation.|\-\-animation-speed-factor scene.animation.time|double
optional
load|Set the animation time to load.|\-\-animation-time scene.animation.frame_rate|double
60
render|Set the animation frame rate used to play the animation interactively.|\-\-animation-frame-rate -scene.camera.index|int
optional
load|Select the scene camera to use when available in the file.
Any negative value means automatic camera.
The default scene always uses automatic camera.|\-\-camera-index -scene.up_direction|string
+Y
load|Define the Up direction|\-\-up +scene.camera.index|int
optional
load|Select the scene camera to use when available in the file.
The default scene always uses automatic camera.|\-\-camera-index +scene.up_direction|string
+Y
load|Define the Up direction. It impacts the grid, the axis, the HDRI and the camera.|\-\-up scene.camera.orthographic|bool
false
load|Toggles between orthographic projection and parallel mode.|\-\-camera\-orthographic ## Interactor Options @@ -42,17 +42,17 @@ interactor.trackball|bool
false
render|Enable trackball interaction.|\-\-t Option|Type
Default
Trigger|Description|F3D option :---:|:---:|:---|:---: -model.matcap.texture|string
-
render|Path to a texture file containing a material capture. All other model options for surfaces are ignored if this is set.|\-\-texture-matcap -model.color.opacity|double
1.0
render|Set *opacity* on the geometry. Usually used with Depth Peeling option. Multiplied with the `model.color.texture` when present.|\-\-opacity -model.color.rgb|vector\
1.0,1.0,1.0
render|Set a *color* on the geometry. Multiplied with the `model.color.texture` when present.|\-\-color -model.color.texture|string
-
render|Path to a texture file that sets the color of the object. Will be multiplied with rgb and opacity.|\-\-texture-base-color -model.emissive.factor|vector\
1.0,1.0,1.0
render| Multiply the emissive color when an emissive texture is present.|\-\-emissive-factor -model.emissive.texture|string
-
render|Path to a texture file that sets the emitted light of the object. Multiplied with the `model.emissive.factor`.|\-\-texture-emissive -model.material.metallic|double
0.0
render|Set the *metallic coefficient* on the geometry (0.0-1.0). Multiplied with the `model.material.texture` when present.|\-\-metallic -model.material.roughness|double
0.3
render|Set the *roughness coefficient* on the geometry (0.0-1.0). Multiplied with the `model.material.texture` when present.|\-\-roughness -model.material.texture|string
-
render|Path to a texture file that sets the Occlusion, Roughness and Metallic values of the object. Multiplied with the `model.material.roughness` and `model.material.metallic`, set both of them to 1.0 to get a true result.|\-\-texture-material -model.normal.scale|double
1.0
render|Normal scale affects the strength of the normal deviation from the normal texture.|\-\-normal-scale -model.normal.texture|string
-
render|Path to a texture file that sets the normal map of the object.|\-\-texture-normal +model.matcap.texture|string
optional
render|Path to a texture file containing a material capture. All other model options for surfaces are ignored if this is set. Model specified by default.|\-\-texture-matcap +model.color.opacity|double
optional
render|Set *opacity* on the geometry. Usually used with Depth Peeling option. Multiplied with the `model.color.texture` when present. Model specified by default.|\-\-opacity +model.color.rgb|vector\
optional
render|Set a *color* on the geometry. Multiplied with the `model.color.texture` when present. Model specified by default.|\-\-color +model.color.texture|string
optional
render|Path to a texture file that sets the color of the object. Will be multiplied with rgb and opacity. Model specified by default.|\-\-texture-base-color +model.emissive.factor|vector\
optional
render| Multiply the emissive color when an emissive texture is present. Model specified by default.|\-\-emissive-factor +model.emissive.texture|string

render|Path to a texture file that sets the emitted light of the object. Multiplied with the `model.emissive.factor`. Model specified by default.|\-\-texture-emissive +model.material.metallic|double
optional
render|Set the *metallic coefficient* on the geometry (0.0-1.0). Multiplied with the `model.material.texture` when present. Model specified by default.|\-\-metallic +model.material.roughness|double
optional
render|Set the *roughness coefficient* on the geometry (0.0-1.0). Multiplied with the `model.material.texture` when present. Model specified by default.|\-\-roughness +model.material.texture|string
optional
render|Path to a texture file that sets the Occlusion, Roughness and Metallic values of the object. Multiplied with the `model.material.roughness` and `model.material.metallic`, set both of them to 1.0 to get a true result. Model specified by default.|\-\-texture-material +model.normal.scale|double
optional
render|Normal scale affects the strength of the normal deviation from the normal texture. Model specified by default.|\-\-normal-scale +model.normal.texture|string
optional
render|Path to a texture file that sets the normal map of the object. Model specified by default.|\-\-texture-normal model.scivis.enable|bool
false
render|*Color by an array* present in on the data. If `model.scivis.array_name` is not set, the first available array will be used.|\-\-scalar-coloring model.scivis.cells|bool
false
render|Color the data with value found *on the cells* instead of points|\-\-cells model.scivis.colormap|vector\
\
render|Set a *custom colormap for the coloring*.
This is a list of colors in the format `val1,red1,green1,blue1,...,valN,redN,greenN,blueN`
where all values are in the range (0,1).|\-\-colormap @@ -62,7 +62,7 @@ model.scivis.range|vector\
optional
render|Set the *coloring rang model.point_sprites.enable|bool
false
render|Show sphere *points sprites* instead of the geometry.|\-\-point-sprites model.point_sprites.type|string
sphere
render|Set the sprites type when showing point sprites (can be `sphere` or `gaussian`).|\-\-point-stripes-type model.point_sprites.size|double
10.0
render|Set the *size* of point sprites.|\-\-point-stripes-size -model.volume.enable|bool
false
render|Enable *volume rendering*. It is only available for 3D image data (vti, dcm, nrrd, mhd files) and will display nothing with other default scene formats.|\-\-volume +model.volume.enable|bool
false
render|Enable *volume rendering*. It is only available for 3D image data (vti, dcm, nrrd, mhd files) and will display nothing with other formats.|\-\-volume model.volume.inverse|bool
false
render|Inverse the linear opacity function.|\-\-inverse ## Render Options diff --git a/doc/libf3d/OVERVIEW.md b/doc/libf3d/OVERVIEW.md index ab17feb562..4f4bcfc74c 100644 --- a/doc/libf3d/OVERVIEW.md +++ b/doc/libf3d/OVERVIEW.md @@ -8,7 +8,7 @@ libf3d API is still in alpha version and may change drastically in the future. ## Getting Started -Rendering a full scene file and starting the interaction is very easy: +Rendering a file and starting the interaction is very easy: ```cpp #include @@ -21,14 +21,14 @@ f3d::engine::autoloadPlugins(); // Create a f3d::engine f3d::engine eng(); -// Load a scene -eng.getLoader().loadScene("path/to/file.ext"); +// Add a file into a scene +eng.getLoader().add("path/to/file.ext"); // Start rendering and interacting eng.getInteractor().start(); ``` -As well as loading multiple geometries into a default scene: +As well as loading multiple files: ```cpp #include @@ -42,7 +42,7 @@ f3d::engine::autoloadPlugins(); f3d::engine eng(); // Load multiples geometries -eng.getLoader().loadGeometry("path/to/file.ext").loadGeometry("path/to/file2.ext"); +eng.getLoader().add({"path/to/file.ext", "path/to/file2.ext"}); // Start rendering and interacting eng.getInteractor().start(); @@ -63,7 +63,7 @@ f3d::mesh_t mesh = {}; mesh.points = { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }; mesh.face_sides = { 3 }; mesh.face_indices = { 0, 1, 2 }; -eng.getLoader().loadGeometry(mesh); +eng.getLoader().add(mesh); // Start rendering and interacting eng.getInteractor().start(); @@ -84,7 +84,7 @@ f3d::engine::autoloadPlugins(); f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); // Load a geometry -eng.getLoader().loadGeometry("path/to/file.ext"); +eng.getLoader().add("path/to/file.ext"); // Set the window size and render to an image f3d::image img = eng.getWindow().setSize(300, 300).renderToImage(); @@ -113,7 +113,7 @@ opt.render.effect.ambient_occlusion = true; opt.render.effect.anti_aliasing = true; // Standard libf3d usage -eng.getLoader().loadGeometry("path/to/file.ext"); +eng.getLoader().add("path/to/file.ext"); eng.getInteractor().start(); ``` Most options are dynamic, some are only taken into account when loading a file. See the [options](OPTIONS.md) documentation. diff --git a/doc/user/INTERACTIONS.md b/doc/user/INTERACTIONS.md index 18a5cd5fa2..beef373793 100644 --- a/doc/user/INTERACTIONS.md +++ b/doc/user/INTERACTIONS.md @@ -72,8 +72,8 @@ Other hotkeys are available: * Space: play the animation if any. * : load the previous file if any and reset the camera. * : load the next file if any and reset the camera. -* : reload the current file. -* : add current file parent directory to the list of files, reload the current file and reset the camera. +* : reload the currently loaded files. +* : add all current files parent directories to the list of files, reload the currently loaded files and reset the camera. * F12: take a screenshot, ie. render the current view to an image file. * F11: take a "minimal" screenshot, ie. render the current view with no grid and no overlays to an image file with a transparent background. @@ -81,7 +81,7 @@ When loading another file or reloading, options that have been changed interacti ## Cycling Coloring -When using the default scene, the following hotkeys let you cycle the coloring of the data: +The following hotkeys let you cycle the coloring of the data: * C: cycle between point data and cell data - field data is not supported. * S: cycle the array available on the currently selected data in alphabetical order, diff --git a/doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md b/doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md index 5b1850335c..e2bdaccef6 100644 --- a/doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md +++ b/doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md @@ -7,6 +7,7 @@ Here is a non exhaustive list of F3D limitations: * Drag and drop interaction cannot be recorded nor played back. * Volume rendering and HDRI support requires a decent GPU. * The `--camera-zoom-factor` option require VTK >= 9.3.0 +* Information about the failure to load a file is not provided before VTK >= 9.4.0 ## Assimp FBX, DAE, OFF, DXF, X and 3MF file formats rely on [Assimp](https://github.com/assimp/assimp) library. It comes with some known limitations: diff --git a/doc/user/OPTIONS.md b/doc/user/OPTIONS.md index 6df488c502..5fb2aa09ae 100644 --- a/doc/user/OPTIONS.md +++ b/doc/user/OPTIONS.md @@ -30,8 +30,7 @@ Options|Default|Description \-\-verbose=\<[debug\|info\|warning\|error\|quiet]\>|info| Set *verbose* level, in order to provide more information about the loaded data in the console output. If no level is provided, assume `debug`. Option parsing may ignore this flag. \-\-progress||Show a *progress bar* when loading the file. \-\-animation-progress||Show a *progress bar* when playing the animation. -\-\-geometry-only||For certain **full scene** file formats (gltf/glb and obj),
reads *only the geometry* from the file and use default scene construction instead. -\-\-group-geometries||When opening multiple files, show them all in the same scene.
Force geometry-only. The configuration file for the first file will be loaded. +\-\-multi-file-mode=\||When opening multiple files, select if they should be grouped (`all`) or alone (`single`). Configuration files for all loaded files will be used in the order they are provided. \-\-up=\<[+\|-][X\|Y\|Z]\>|+Y|Define the Up direction. -x, \-\-axis||Show *axes* as a trihedron in the scene. -g, \-\-grid||Show *a grid* aligned with the horizontal (orthogonal to the Up direction) plane. @@ -58,17 +57,17 @@ Options|Default|Description \-\-point-size=\||Set the *size* of points when showing vertices. Model specified by default. \-\-line-width=\||Set the *width* of lines when showing edges. Model specified by default. \-\-backface-type=\||Set the Backface type. Model specified by default. -\-\-color=\|1.0, 1.0, 1.0| Set a *color* on the geometry. Multiplied with the base color texture when present.
Requires a default scene. -\-\-opacity=\|1.0|Set *opacity* on the geometry. Multiplied with the base color texture when present.
Requires a default scene. Usually used with Depth Peeling option. -\-\-roughness=\|0.3|Set the *roughness coefficient* on the geometry (0.0-1.0). Multiplied with the material texture when present.
Requires a default scene. -\-\-metallic=\|0.0|Set the *metallic coefficient* on the geometry (0.0-1.0). Multiplied with the material texture when present.
Requires a default scene. +\-\-color=\|1.0, 1.0, 1.0| Set a *color* on the geometry. Multiplied with the base color texture when present.
Model specified by default. +\-\-opacity=\|1.0|Set *opacity* on the geometry. Multiplied with the base color texture when present.
Model specified by default. Usually used with Depth Peeling option. +\-\-roughness=\|0.3|Set the *roughness coefficient* on the geometry (0.0-1.0). Multiplied with the material texture when present.
Model specified by default. +\-\-metallic=\|0.0|Set the *metallic coefficient* on the geometry (0.0-1.0). Multiplied with the material texture when present.
Model specified by default. \-\-hdri-file=\||Set the *HDRI* image that can be used as ambient lighting and skybox.
Valid file format are hdr, exr, png, jpg, pnm, tiff, bmp.
If not set, a default is provided. \-\-hdri-ambient||Light the scene using the *HDRI* image as ambient lighting.
The environment act as a light source and is reflected on the material. -\-\-texture-matcap=\||Set the texture file to control the material capture of the object. All other model options for surfaces are ignored if this is set. Must be in linear color space. -\-\-texture-base-color=\||Set the texture file to control the color of the object. Please note this will be multiplied with the color and opacity options. Must be in sRGB color space. -\-\-texture-material=\||Set the texture file to control the occlusion, roughness and metallic values of the object. Please note this will be multiplied with the roughness and metallic options, which have impactful default values. To obtain true results, use \-\-roughness=1 \-\-metallic=1. Must be in linear color space. -\-\-texture-emissive=\||Set the texture file to control the emitted light of the object. Please note this will be multiplied with the emissive factor. Must be in sRGB color space. -\-\-emissive-factor=\|1.0, 1.0, 1.0|Set the emissive factor. This value is multiplied with the emissive color when an emissive texture is present. +\-\-texture-matcap=\||Set the texture file to control the material capture of the object. All other model options for surfaces are ignored if this is set. Must be in linear color space.
Model specified by default. +\-\-texture-base-color=\||Set the texture file to control the color of the object. Please note this will be multiplied with the color and opacity options. Must be in sRGB color space.
Model specified by default. +\-\-texture-material=\||Set the texture file to control the occlusion, roughness and metallic values of the object. Please note this will be multiplied with the roughness and metallic options, which have impactful default values. To obtain true results, use \-\-roughness=1 \-\-metallic=1. Must be in linear color space.
Model specified by default. +\-\-texture-emissive=\||Set the texture file to control the emitted light of the object. Please note this will be multiplied with the emissive factor. Must be in sRGB color space.
Model specified by default. +\-\-emissive-factor=\|1.0, 1.0, 1.0|Set the emissive factor. This value is multiplied with the emissive color when an emissive texture is present.
Model specified by default. ## Window options @@ -79,7 +78,7 @@ Options|Default|Description \-\-position=\||Set the *window position* (top left corner) , in pixels, starting from the top left of your screens. -z, \-\-fps||Display a *frame per second counter*. -n, \-\-filename||Display the *name of the file* on top of the window. --m, \-\-metadata||Display the *metadata*.
Empty without a default scene. +-m, \-\-metadata||Display the *metadata*. \-\-hdri-skybox||Show the HDRI as a skybox. Overrides \-\-bg-color and \-\-no-background. -u, \-\-blur-background||Blur background.
Useful with a HDRI skybox. \-\-blur-coc|20|Blur circle of confusion radius. @@ -89,7 +88,7 @@ Options|Default|Description Options|Default|Description ------|------|------ --s, \-\-scalar-coloring||Enable scalar coloring if present in the file. If `--coloring-array` is not set, the first in alphabetical order will be picked if any are available.
Requires a default scene. +-s, \-\-scalar-coloring||Enable scalar coloring if present in the file. If `--coloring-array` is not set, the first in alphabetical order will be picked if any are available. \-\-coloring-array=\||The coloring array name to use when coloring.
Use \-\-verbose to recover the usable array names. -y, \-\-comp=\|-1|Specify the *component from the scalar* array to color with.
Use with the scalar option. -1 means *magnitude*. -2 or the short option, -y, means *direct values*.
When using *direct values*, components are used as L, LA, RGB, RGBA values depending on the number of components. -c, \-\-cells||Specify that the scalar array is to be found *on the cells* instead of on the points.
Use with the scalar option. @@ -173,3 +172,5 @@ The destination filename used by `--output` or to save screenshots can use the f For example the screenshot filename is configured as `{app}/{model}_{n}.png` by default, meaning that, assuming the model `hello.glb` is being viewed, consecutive screenshots are going to be saved as `F3D/hello_1.png`, `F3D/hello_2.png`, `F3D/hello_3.png`, ... + +Model related variables will be replaced by `no_file` if no file is loaded and `multi_file` if multiple files are loaded using the `multi-file-mode` option. diff --git a/doc/user/USAGE.md b/doc/user/USAGE.md index 5bffe6aebf..f821173b10 100644 --- a/doc/user/USAGE.md +++ b/doc/user/USAGE.md @@ -28,8 +28,8 @@ Here is the list of supported file formats: * **.xbf** : Open CASCADE XBF format * **.abc** : Alembic format * **.vdb** : VDB format (experimental) -* **.obj** : Wavefront OBJ file format (full scene and default scene) -* **.gltf/.glb** : GL Transmission Format (full scene and default scene) +* **.obj** : Wavefront OBJ file format (full scene) +* **.gltf/.glb** : GL Transmission Format (full scene) * **.3ds** : Autodesk 3D Studio file format (full scene) * **.wrl** : VRML file format (full scene) * **.fbx** : Autodesk Filmbox (full scene) @@ -44,12 +44,19 @@ Here is the list of supported file formats: The **full scene** formats (.gltf/.glb, .3ds, .wrl, .obj, .fbx, .dae, .off, .x, .3mf, .usd) contain not only *geometry*, but also some scene information like *lights*, *cameras*, *actors* in the scene, as well as *texture* properties. -By default, all this information will be loaded from the file and displayed. Use the `--geometry-only` [options](OPTIONS.md) -to modify this behavior. For file formats that do not support it, **a default scene** is created. +By default, all this information will be loaded from the file and displayed. +For file formats that do not support it, **a default scene** is created. For **default scene** formats, certain default values are set automatically: - - line_width: 1.0 - - point_size: 10.0 + - texture-*: "" + - line-width: 1.0 + - point-size: 10.0 + - opacity: 1.0 + - color: 1.0, 1.0, 1.0 + - emissive-factor: 1.0, 1.0, 1.0 + - normal-scale: 1.0 + - metallic: 0.0 + - roughness: 0.3 They will be overridden when using corresponding [options](OPTIONS.md). diff --git a/examples/libf3d/cpp/multi-geom/main.cxx b/examples/libf3d/cpp/multi-geom/main.cxx index 9b9d589055..8d783a5141 100644 --- a/examples/libf3d/cpp/multi-geom/main.cxx +++ b/examples/libf3d/cpp/multi-geom/main.cxx @@ -26,7 +26,7 @@ int main(int argc, char** argv) f3d::loader& load = eng.getLoader(); for (auto& entry : std::filesystem::directory_iterator(argv[1])) { - load.loadGeometry(entry.path().string()); + load.add(entry.path().string()); } // Render diff --git a/examples/libf3d/cpp/render-image/main.cxx b/examples/libf3d/cpp/render-image/main.cxx index d5b0aa98c3..2ea1939e96 100644 --- a/examples/libf3d/cpp/render-image/main.cxx +++ b/examples/libf3d/cpp/render-image/main.cxx @@ -22,7 +22,7 @@ int main(int argc, char** argv) f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); // Load a model - eng.getLoader().loadGeometry(std::string(argv[1])); + eng.getLoader().add(std::string(argv[1])); // Set the window size and render to an image f3d::image img = eng.getWindow().setSize(300, 300).renderToImage(); diff --git a/examples/libf3d/cpp/render-interact/main.cxx b/examples/libf3d/cpp/render-interact/main.cxx index 3e20259df1..09fe31ad84 100644 --- a/examples/libf3d/cpp/render-interact/main.cxx +++ b/examples/libf3d/cpp/render-interact/main.cxx @@ -21,7 +21,7 @@ int main(int argc, char** argv) f3d::engine eng(f3d::window::Type::NATIVE); // Load a model - const f3d::loader& load = eng.getLoader().loadGeometry(std::string(argv[1])); + const f3d::loader& load = eng.getLoader().add(std::string(argv[1])); // Render f3d::window& win = eng.getWindow(); diff --git a/examples/libf3d/cpp/use-options-string/main.cxx b/examples/libf3d/cpp/use-options-string/main.cxx index 56a0ff242c..ef2fa71d07 100644 --- a/examples/libf3d/cpp/use-options-string/main.cxx +++ b/examples/libf3d/cpp/use-options-string/main.cxx @@ -28,7 +28,7 @@ int main(int argc, char** argv) .setAsString("render.grid.enable", "1"); // Load a model - const f3d::loader& load = eng.getLoader().loadGeometry(std::string(argv[1])); + const f3d::loader& load = eng.getLoader().add(std::string(argv[1])); // Render f3d::window& win = eng.getWindow(); diff --git a/examples/libf3d/cpp/use-options-struct/main.cxx b/examples/libf3d/cpp/use-options-struct/main.cxx index f78ac1cf31..894ac94f09 100644 --- a/examples/libf3d/cpp/use-options-struct/main.cxx +++ b/examples/libf3d/cpp/use-options-struct/main.cxx @@ -28,7 +28,7 @@ int main(int argc, char** argv) opt.render.grid.enable = true; // Load a model - const f3d::loader& load = eng.getLoader().loadGeometry(std::string(argv[1])); + const f3d::loader& load = eng.getLoader().add(std::string(argv[1])); // Render f3d::window& win = eng.getWindow(); diff --git a/examples/libf3d/cpp/use-options-variant/main.cxx b/examples/libf3d/cpp/use-options-variant/main.cxx index 6118d256fa..dad4bc01c9 100644 --- a/examples/libf3d/cpp/use-options-variant/main.cxx +++ b/examples/libf3d/cpp/use-options-variant/main.cxx @@ -26,7 +26,7 @@ int main(int argc, char** argv) opt.set("render.show_edges", true).set("render.line_width", 10.0).set("render.grid.enable", true); // Load a model - const f3d::loader& load = eng.getLoader().loadGeometry(std::string(argv[1])); + const f3d::loader& load = eng.getLoader().add(std::string(argv[1])); // Render f3d::window& win = eng.getWindow(); diff --git a/java/F3DJavaBindings.cxx b/java/F3DJavaBindings.cxx index dc56f2f714..ae6bfdb599 100644 --- a/java/F3DJavaBindings.cxx +++ b/java/F3DJavaBindings.cxx @@ -55,17 +55,15 @@ extern "C" } // Loader - JNIEXPORT void JAVA_BIND(Loader, loadScene)(JNIEnv* env, jobject self, jstring path) + JNIEXPORT void JAVA_BIND(Loader, add)(JNIEnv* env, jobject self, jstring path) { const char* str = env->GetStringUTFChars(path, nullptr); - GetEngine(env, self)->getLoader().loadScene(str); + GetEngine(env, self)->getLoader().add(str); env->ReleaseStringUTFChars(path, str); } - JNIEXPORT void JAVA_BIND(Loader, loadGeometry)(JNIEnv* env, jobject self, jstring path) + JNIEXPORT void JAVA_BIND(Loader, clear)(JNIEnv* env, jobject self) { - const char* str = env->GetStringUTFChars(path, nullptr); - GetEngine(env, self)->getLoader().loadGeometry(str); - env->ReleaseStringUTFChars(path, str); + GetEngine(env, self)->getLoader().clear(); } // Window diff --git a/java/Loader.java b/java/Loader.java index 6f63704002..1566d7f0bd 100644 --- a/java/Loader.java +++ b/java/Loader.java @@ -6,8 +6,8 @@ public Loader(long nativeAddress) { mNativeAddress = nativeAddress; } - public native void loadScene(String file); - public native void loadGeometry(String file); + public native void add(String file); + public native void clear(); private long mNativeAddress; } diff --git a/java/testing/TestJavaBindings.java b/java/testing/TestJavaBindings.java index 384315e70c..4f7d381c1a 100644 --- a/java/testing/TestJavaBindings.java +++ b/java/testing/TestJavaBindings.java @@ -29,7 +29,7 @@ public static void main(String[] args) { assert pos[2] == 2.0 : "Position Z is not valid"; Loader loader = engine.getLoader(); - loader.loadGeometry(args[0] + "data/cow.vtp"); + loader.add(args[0] + "data/cow.vtp"); } } } diff --git a/library/options.json b/library/options.json index 4f843ddce8..17703c9d3b 100644 --- a/library/options.json +++ b/library/options.json @@ -187,56 +187,45 @@ "model": { "matcap": { "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "color": { "opacity": { - "type": "double", - "default_value": "1.0" + "type": "double" }, "rgb": { - "type": "double_vector", - "default_value": "1.0, 1.0, 1.0" + "type": "double_vector" }, "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "emissive": { "factor": { - "type": "double_vector", - "default_value": "1.0, 1.0, 1.0" + "type": "double_vector" }, "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "normal": { "scale": { - "type": "double", - "default_value": "1.0" + "type": "double" }, "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "material": { "metallic": { - "type": "double", - "default_value": "0.0" + "type": "double" }, "roughness": { - "type": "double", - "default_value": "0.3" + "type": "double" }, "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "scivis": { diff --git a/library/private/animationManager.h b/library/private/animationManager.h index cf2c19d841..032946ddbd 100644 --- a/library/private/animationManager.h +++ b/library/private/animationManager.h @@ -29,16 +29,27 @@ class interactor_impl; class animationManager { public: - animationManager() = default; + animationManager(const options& options, window& window); ~animationManager() = default; + /** + * Set the interactor to use in the animation_manager, should be set before initializing if any + */ + void SetInteractor(interactor_impl* interactor); + + /** + * Set the importer to use in the animation_manager, must be set before initializing + */ + void SetImporter(vtkImporter* importer); + /** * Initialize the animation manager, required before playing the animation. - * Provided pointers are expected to be not null except interactor. + * Can be used to reset animation to the initial state. + * Importer must be set before use. + * Interactor should be set before use if any. * Return true if at least one animation is available, false otherwise. */ - bool Initialize( - const options* options, window* window, interactor_impl* interactor, vtkImporter* importer); + bool Initialize(); /** * Start/Stop playing the animation @@ -89,16 +100,16 @@ class animationManager */ void GetTimeRange(double timeRange[2]); -protected: +private: /** * Called by an internal timer to advance one animation tick */ void Tick(); + const options& Options; + window& Window; vtkImporter* Importer = nullptr; - window* Window = nullptr; interactor_impl* Interactor = nullptr; - const options* Options = nullptr; double TimeRange[2] = { 0.0, 0.0 }; bool Playing = false; diff --git a/library/private/loader_impl.h b/library/private/loader_impl.h index 690d86fd28..94695bc68e 100644 --- a/library/private/loader_impl.h +++ b/library/private/loader_impl.h @@ -30,11 +30,12 @@ class loader_impl : public loader */ loader_impl(const options& options, window_impl& window); ~loader_impl(); - loader& loadGeometry(const std::string& filePath, bool reset) override; - loader& loadGeometry(const mesh_t& mesh, bool reset = false) override; - loader& loadScene(const std::string& filePath) override; - bool hasGeometryReader(const std::string& filePath) override; - bool hasSceneReader(const std::string& filePath) override; + loader& add(const std::filesystem::path& filePath) override; + loader& add(const std::vector& filePath) override; + loader& add(const std::vector& filePathStrings) override; + loader& add(const mesh_t& mesh) override; + loader& clear() override; + bool supports(const std::filesystem::path& filePath) override; ///@} /** diff --git a/library/private/window_impl.h b/library/private/window_impl.h index 43da739a42..0f62a41187 100644 --- a/library/private/window_impl.h +++ b/library/private/window_impl.h @@ -16,7 +16,7 @@ #include class vtkRenderWindow; -class vtkF3DGenericImporter; +class vtkF3DMetaImporter; namespace f3d { class options; @@ -58,27 +58,31 @@ class window_impl : public window /** * Implementation only API. - * Create and initialize the internal vtkF3DRenderer with the provided parameters - * Called by the loader right before reading a file + * Initialize the renderer by clearing it of all actors. */ - virtual void Initialize(bool withColoring); + void Initialize(); /** * Implementation only API. - * Set the importer on an already created vtkF3DRendererWithColoring - * Called by the loader right after reading a file + * Initialize the up vector on the renderer using the Up string option */ - virtual void SetImporterForColoring(vtkF3DGenericImporter* importer); + void InitializeUpVector(); + + /** + * Implementation only API. + * Set the importer on the internal renderer + */ + void SetImporter(vtkF3DMetaImporter* importer); /** * Implementation only API. * Use all the rendering related options to update the configuration of the window - * and the rendering stack below. This also initialize the rendering stack if needed. + * and the rendering stack below. * This will be called automatically when calling loader::loadFile but can also be called manually * when needed. This must be called, either manually or automatically, before any render call. * Return true on success, false otherwise. */ - virtual void UpdateDynamicOptions(); + void UpdateDynamicOptions(); /** * Implementation only API. diff --git a/library/public/engine.h b/library/public/engine.h index 9503e9bdb0..db486c065a 100644 --- a/library/public/engine.h +++ b/library/public/engine.h @@ -22,26 +22,15 @@ namespace f3d * Configured on creation using an enum, then all objects * can be accessed through their getter. * - * Example usage for a default scene file: + * Example usage for adding some files in the scene * * \code{.cpp} - * f3d::engine eng(f3d::engine::CREATE_WINDOW | f3d::engine::CREATE_INTERACTOR); + * f3d::engine eng(); * f3d::loader& load = eng.getLoader(); - * load.loadGeometry("path/to/file").loadGeometry("path/to/another/file"); + * load.add({"path/to/file", "path/to/another/file"}); * f3d::interactor& inter = eng.getInteractor(); * inter.start(); * \endcode - * - * Example usage for a full scene file: - * - * \code{.cpp} - * f3d::engine eng(f3d::engine::CREATE_WINDOW | f3d::engine::CREATE_INTERACTOR); - * f3d::loader& load = eng.getLoader(); - * load.loadScene("path/to/file"); - * f3d::interactor& inter = eng.getInteractor(); - * inter.start(); - * \endcode - */ class F3D_EXPORT engine { diff --git a/library/public/loader.h b/library/public/loader.h index 3624aec6df..2f7f74cf1c 100644 --- a/library/public/loader.h +++ b/library/public/loader.h @@ -5,6 +5,7 @@ #include "export.h" #include "types.h" +#include #include #include @@ -24,13 +25,9 @@ namespace f3d * f3d::engine eng(f3d::window::Type::NATIVE); * f3d::loader& load = eng.getLoader(); * - * if (load.hasSceneReader(path) + * if (load.supports(path) * { - * load.loadScene(path); - * } - * else if (load.hasGeometryReader(path) - * { - * load.loadGeometry(path); + * load.add(path); * } * \endcode * @@ -48,41 +45,44 @@ class F3D_EXPORT loader : exception(what){}; }; + ///@{ /** - * Return true if the loader has a geometry reader for the providen file, false otherwise. + * Add and load provided files into the scene + * Already added file will NOT be reloaded */ - virtual bool hasGeometryReader(const std::string& filePath) = 0; + virtual loader& add(const std::filesystem::path& filePath) = 0; + virtual loader& add(const std::vector& filePath) = 0; + virtual loader& add(const std::vector& filePathStrings) = 0; + ///@} /** - * Load a geometry from a provided file to the scene. - * Calling this method will reset the scene before loading if a full scene was loaded previously - * or if the reset argument is set to true, It will not reset if only geometries were loaded - * previously. Geometries loaded using this method will be available in a default scene and use - * all default scene related options. Throw a load_failure_exception on failure. + * Add and load provided mesh into the scene */ - virtual loader& loadGeometry(const std::string& filePath, bool reset = false) = 0; + virtual loader& add(const mesh_t& mesh) = 0; + ///@{ /** - * Load a geometry from memory buffers. - * Calling this method will reset the scene before loading if a full scene was loaded previously - * or if the reset argument is set to true, It will not reset if only geometries were loaded - * previously. Geometries loaded using this method will be available in a default scene and use - * all default scene related options. - * Throw a load_failure_exception if the mesh is invalid. + * Convenience initializer list signature for add method */ - virtual loader& loadGeometry(const mesh_t& mesh, bool reset = false) = 0; + loader& add(std::initializer_list list) + { + return this->add(std::vector(list)); + } + loader& add(std::initializer_list list) + { + return this->add(std::vector(list)); + } + ///@} /** - * Return true if the loader has a scene reader for the providen file, false otherwise. + * Clear the scene of all added files */ - virtual bool hasSceneReader(const std::string& filePath) = 0; + virtual loader& clear() = 0; /** - * Reset scene and load a (full) scene from provided file. - * Please note default scene related options are not taken into account when loading a full scene. - * Throw a load_failure_exception on failure. + * Return true if provided file path is supported, false otherwise. */ - virtual loader& loadScene(const std::string& filePath) = 0; + virtual bool supports(const std::filesystem::path& filePath) = 0; protected: //! @cond diff --git a/library/src/animationManager.cxx b/library/src/animationManager.cxx index e29c029626..439c4c89d7 100644 --- a/library/src/animationManager.cxx +++ b/library/src/animationManager.cxx @@ -16,26 +16,39 @@ namespace f3d::detail { //---------------------------------------------------------------------------- -bool animationManager::Initialize( - const options* options, window* window, interactor_impl* interactor, vtkImporter* importer) +animationManager::animationManager(const options& options, window& window) + : Options(options) + , Window(window) { - assert(importer); +} + +//---------------------------------------------------------------------------- +void animationManager::SetImporter(vtkImporter* importer) +{ + this->Importer = importer; +} + +//---------------------------------------------------------------------------- +void animationManager::SetInteractor(interactor_impl* interactor) +{ + this->Interactor = interactor; +} + +//---------------------------------------------------------------------------- +bool animationManager::Initialize() +{ + assert(this->Importer); this->HasAnimation = false; this->Playing = false; this->CurrentTime = 0; this->CurrentTimeSet = false; - this->Options = options; - this->Interactor = interactor; - this->Window = window; - this->Importer = importer; // This can be -1 if animation support is not implemented in the importer this->AvailAnimations = this->Importer->GetNumberOfAnimations(); - - if (this->AvailAnimations > 0 && interactor) + if (this->AvailAnimations > 0 && this->Interactor) { this->ProgressWidget = vtkSmartPointer::New(); - interactor->SetInteractorOn(this->ProgressWidget); + this->Interactor->SetInteractorOn(this->ProgressWidget); vtkProgressBarRepresentation* progressRep = vtkProgressBarRepresentation::SafeDownCast(this->ProgressWidget->GetRepresentation()); @@ -50,7 +63,7 @@ bool animationManager::Initialize( progressRep->SetShowBorderToOff(); progressRep->DrawFrameOff(); progressRep->SetPadding(0.0, 0.0); - progressRep->SetVisibility(options->ui.animation_progress); + progressRep->SetVisibility(this->Options.ui.animation_progress); this->ProgressWidget->On(); } else @@ -60,12 +73,12 @@ bool animationManager::Initialize( if (this->AvailAnimations <= 0) { - log::debug("No animation available in this file"); - if (options->scene.animation.index > 0) + log::debug("No animation available"); + if (this->Options.scene.animation.index > 0) { log::warn("An animation index has been specified but there are no animation available."); } - if (options->scene.animation.time.has_value()) + if (this->Options.scene.animation.time.has_value()) { log::warn("No animation available, cannot load a specific animation time"); } @@ -74,15 +87,14 @@ bool animationManager::Initialize( } else { - log::debug("Animation(s) available in this file are:"); + log::debug("Animation(s) available are:"); } for (int i = 0; i < this->AvailAnimations; i++) { log::debug(i, ": ", this->Importer->GetAnimationName(i)); } - log::debug(""); - this->AnimationIndex = options->scene.animation.index; + this->AnimationIndex = this->Options.scene.animation.index; if (this->AnimationIndex > 0 && this->AnimationIndex >= this->AvailAnimations) { log::warn( @@ -106,7 +118,7 @@ bool animationManager::Initialize( // Discard timesteps, F3D only cares about real elapsed time using time range // Specifying the frame rate in the next call is not needed after VTK 9.2.20230603 : // VTK_VERSION_CHECK(9, 2, 20230603) - double frameRate = options->scene.animation.frame_rate; + double frameRate = this->Options.scene.animation.frame_rate; this->Importer->GetTemporalInformation( animIndex, frameRate, nbTimeSteps, timeRange, timeSteps); @@ -127,8 +139,9 @@ bool animationManager::Initialize( { log::debug("Animation(s) time range is: [", this->TimeRange[0], ", ", this->TimeRange[1], "]."); } + log::debug(""); - bool autoplay = options->scene.animation.autoplay; + bool autoplay = this->Options.scene.animation.autoplay; if (autoplay) { this->StartAnimation(); @@ -177,12 +190,12 @@ void animationManager::ToggleAnimation() // Always reset previous tick when starting the animation this->PreviousTick = std::chrono::steady_clock::now(); - double frameRate = this->Options->scene.animation.frame_rate; + double frameRate = this->Options.scene.animation.frame_rate; this->CallBackId = this->Interactor->createTimerCallBack(1000.0 / frameRate, [this]() { this->Tick(); }); } - if (this->Playing && this->Options->scene.camera.index.has_value()) + if (this->Playing && this->Options.scene.camera.index.has_value()) { this->Interactor->disableCameraMovement(); } @@ -206,7 +219,7 @@ void animationManager::Tick() // Convert to a usable time in seconds double elapsedTime = static_cast(timeInMS) / 1000.0; - double animationSpeedFactor = this->Options->scene.animation.speed_factor; + double animationSpeedFactor = this->Options.scene.animation.speed_factor; // elapsedTime can be negative elapsedTime *= animationSpeedFactor; @@ -226,7 +239,7 @@ void animationManager::Tick() if (this->LoadAtTime(this->CurrentTime)) { - this->Window->render(); + this->Window.render(); } } @@ -306,7 +319,8 @@ int animationManager::GetAnimationIndex() // --------------------------------------------------------------------------------- std::string animationManager::GetAnimationName() { - if (!this->Importer || this->AvailAnimations <= 0) + assert(this->Importer); + if (this->AvailAnimations <= 0) { return "No animation"; } diff --git a/library/src/interactor_impl.cxx b/library/src/interactor_impl.cxx index 2157086605..2b66f47757 100644 --- a/library/src/interactor_impl.cxx +++ b/library/src/interactor_impl.cxx @@ -395,19 +395,12 @@ class interactor_impl::internals return; } - // No user defined behavior, load the first file + // No user defined behavior, load all files if (!filesVec.empty()) { assert(self->AnimationManager); self->AnimationManager->StopAnimation(); - if (self->Loader.hasSceneReader(filesVec[0])) - { - self->Loader.loadScene(filesVec[0]); - } - else if (self->Loader.hasGeometryReader(filesVec[0])) - { - self->Loader.loadGeometry(filesVec[0], true); - } + self->Loader.add(filesVec); self->Window.render(); } } diff --git a/library/src/loader_impl.cxx b/library/src/loader_impl.cxx index f2ecbaa91a..979737ceea 100644 --- a/library/src/loader_impl.cxx +++ b/library/src/loader_impl.cxx @@ -9,6 +9,7 @@ #include "factory.h" #include "vtkF3DGenericImporter.h" #include "vtkF3DMemoryMesh.h" +#include "vtkF3DMetaImporter.h" #include #include @@ -21,6 +22,8 @@ #include #include +namespace fs = std::filesystem; + namespace f3d::detail { class loader_impl::internals @@ -29,7 +32,11 @@ class loader_impl::internals internals(const options& options, window_impl& window) : Options(options) , Window(window) + , AnimationManager(options, window) { + this->MetaImporter->SetRenderWindow(this->Window.GetRenderWindow()); + this->Window.SetImporter(this->MetaImporter); + this->AnimationManager.SetImporter(this->MetaImporter); } struct ProgressDataStruct @@ -80,42 +87,19 @@ class loader_impl::internals data->timer->StartTimer(); } - static void DisplayImporterDescription(vtkImporter* importer) + void Load(const std::vector>& importers) { - vtkIdType availCameras = importer->GetNumberOfCameras(); - if (availCameras <= 0) - { - log::debug("No camera available in this file"); - } - else + for (const vtkSmartPointer& importer : importers) { - log::debug("Camera(s) available in this file are:"); + this->MetaImporter->AddImporter(importer); } - for (int i = 0; i < availCameras; i++) - { - log::debug(i, ": ", importer->GetCameraName(i)); - } - log::debug(""); - log::debug(importer->GetOutputsDescription(), "\n"); - } - void Reset() - { - // Reset the generic importer - this->GenericImporter->RemoveInternalReaders(); - - // Remove the importer from the renderer - this->Window.SetImporterForColoring(nullptr); - - // Window initialization is needed - this->Window.Initialize(true); - } + // Initialize the UpVector on load + this->Window.InitializeUpVector(); - void LoadGeometry(const std::string& name, vtkAlgorithm* source, bool reset) - { - if (!this->DefaultScene || reset) + if (this->Options.scene.camera.index.has_value()) { - this->Reset(); + this->MetaImporter->SetCameraIndex(this->Options.scene.camera.index.value()); } // Manage progress bar @@ -127,29 +111,25 @@ class loader_impl::internals if (this->Options.ui.loader_progress && this->Interactor) { loader_impl::internals::CreateProgressRepresentationAndCallback( - &callbackData, this->GenericImporter, this->Interactor); + &callbackData, this->MetaImporter, this->Interactor); } - // Add a single internal reader - this->GenericImporter->AddInternalReader(name, source); - - // Update the importer + // Update the meta importer, the will only update importers that have not been update before #if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) - if (!this->GenericImporter->Update()) + if (!this->MetaImporter->Update()) { - throw loader::load_failure_exception("failed to load geometry: " + name); + throw loader::load_failure_exception("failed to load scene"); } #else - this->GenericImporter->Update(); + this->MetaImporter->Update(); #endif // Remove anything progress related if any - this->GenericImporter->RemoveObservers(vtkCommand::ProgressEvent); + this->MetaImporter->RemoveObservers(vtkCommand::ProgressEvent); progressWidget->Off(); // Initialize the animation using temporal information from the importer - if (this->AnimationManager.Initialize( - &this->Options, &this->Window, this->Interactor, this->GenericImporter)) + if (this->AnimationManager.Initialize()) { if (this->Options.scene.animation.time.has_value()) { @@ -168,202 +148,145 @@ class loader_impl::internals // Set the name for animation this->Window.setAnimationNameInfo(this->AnimationManager.GetAnimationName()); - // Display the importer description - loader_impl::internals::DisplayImporterDescription(this->GenericImporter); - - // Set the importer to use for coloring and actors - this->Window.SetImporterForColoring(this->GenericImporter); + // Display output description + loader_impl::internals::DisplayImporterDescription(this->MetaImporter); - // Initialize renderer and reset camera to bounds + // Update all window options and reset camera to bounds if needed this->Window.UpdateDynamicOptions(); - this->Window.getCamera().resetToBounds(); + if (!this->Options.scene.camera.index.has_value()) + { + this->Window.getCamera().resetToBounds(); + } // Print info about scene and coloring this->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); this->Window.PrintSceneDescription(log::VerboseLevel::DEBUG); + } - this->DefaultScene = true; + static void DisplayImporterDescription(vtkImporter* importer) + { + vtkIdType availCameras = importer->GetNumberOfCameras(); + if (availCameras <= 0) + { + log::debug("No camera available"); + } + else + { + log::debug("Camera(s) available are:"); + } + for (int i = 0; i < availCameras; i++) + { + log::debug(i, ": ", importer->GetCameraName(i)); + } + log::debug(""); + log::debug(importer->GetOutputsDescription(), "\n"); } - bool DefaultScene = false; const options& Options; window_impl& Window; interactor_impl* Interactor = nullptr; animationManager AnimationManager; - vtkSmartPointer CurrentFullSceneImporter; - vtkNew GenericImporter; + vtkNew MetaImporter; }; //---------------------------------------------------------------------------- loader_impl::loader_impl(const options& options, window_impl& window) : Internals(std::make_unique(options, window)) { - // Set render window on generic importer - this->Internals->GenericImporter->SetRenderWindow(this->Internals->Window.GetRenderWindow()); } //---------------------------------------------------------------------------- loader_impl::~loader_impl() = default; //---------------------------------------------------------------------------- -loader& loader_impl::loadGeometry(const std::string& filePath, bool reset) +loader& loader_impl::add(const fs::path& filePath) { - // Check file validity - if (filePath.empty()) - { - // Calling with reset is an usual codepath to reset the window - if (!reset) - { - log::debug("Provided geometry file path is empty\n"); - } - this->Internals->Reset(); - return *this; - } - if (!vtksys::SystemTools::FileExists(filePath, true)) - { - throw loader::load_failure_exception(filePath + " does not exists"); - } + std::vector paths = { filePath }; + return this->add(paths); +} - f3d::reader* reader = f3d::factory::instance()->getReader(filePath); - if (reader) - { - log::debug("Found a reader for \"" + filePath + "\" : \"" + reader->getName() + "\""); - } - else - { - throw loader::load_failure_exception( - filePath + " is not a file of a supported 3D geometry file format"); - } - auto vtkReader = reader->createGeometryReader(filePath); - if (!vtkReader) +//---------------------------------------------------------------------------- +loader& loader_impl::add(const std::vector& filePathStrings) +{ + std::vector paths; + paths.reserve(filePathStrings.size()); + for (const std::string& str : filePathStrings) { - throw loader::load_failure_exception( - filePath + " is not a file of a supported 3D geometry file format for default scene"); + paths.emplace_back(str); } - // Read the file - log::debug("Loading 3D geometry: ", filePath, "\n"); - - this->Internals->LoadGeometry(vtksys::SystemTools::GetFilenameName(filePath), vtkReader, reset); - - return *this; + return this->add(paths); } //---------------------------------------------------------------------------- -loader& loader_impl::loadScene(const std::string& filePath) +loader& loader_impl::add(const std::vector& filePaths) { - if (filePath.empty()) + if (filePaths.empty()) { log::debug("No file to load a full scene provided\n"); return *this; } - if (!vtksys::SystemTools::FileExists(filePath, true)) - { - throw loader::load_failure_exception(filePath + " does not exists"); - } - - // Recover the importer for the provided file path - this->Internals->CurrentFullSceneImporter = nullptr; - f3d::reader* reader = f3d::factory::instance()->getReader(filePath); - if (reader) - { - log::debug("Found a reader for \"" + filePath + "\" : \"" + reader->getName() + "\""); - } - else - { - throw loader::load_failure_exception( - filePath + " is not a file of a supported 3D scene file format"); - } - this->Internals->CurrentFullSceneImporter = reader->createSceneReader(filePath); - if (!this->Internals->CurrentFullSceneImporter) - { - throw loader::load_failure_exception( - filePath + " is not a file of a supported 3D scene file format for full scene"); - } - - this->Internals->Window.Initialize(false); - this->Internals->DefaultScene = false; - // Initialize importer for rendering - this->Internals->CurrentFullSceneImporter->SetRenderWindow( - this->Internals->Window.GetRenderWindow()); - - if (this->Internals->Options.scene.camera.index.has_value()) + std::vector> importers; + for (const fs::path& filePath : filePaths) { - this->Internals->CurrentFullSceneImporter->SetCamera( - this->Internals->Options.scene.camera.index.value()); - } - - log::debug("Loading 3D scene: ", filePath, "\n"); + if (filePath.empty()) + { + log::debug("An empty file to load was provided\n"); + continue; + } + if (!vtksys::SystemTools::FileExists(filePath.string(), true)) + { + throw loader::load_failure_exception(filePath.string() + " does not exists"); + } - // Manage progress bar - vtkNew progressWidget; - vtkNew timer; - loader_impl::internals::ProgressDataStruct callbackData; - callbackData.timer = timer; - callbackData.widget = progressWidget; - if (this->Internals->Options.ui.loader_progress && this->Internals->Interactor) - { - loader_impl::internals::CreateProgressRepresentationAndCallback( - &callbackData, this->Internals->CurrentFullSceneImporter, this->Internals->Interactor); + // Recover the importer for the provided file path + f3d::reader* reader = f3d::factory::instance()->getReader(filePath.string()); + if (reader) + { + log::debug( + "Found a reader for \"" + filePath.string() + "\" : \"" + reader->getName() + "\""); + } + else + { + throw loader::load_failure_exception( + filePath.string() + " is not a file of a supported 3D scene file format"); + } + vtkSmartPointer importer = reader->createSceneReader(filePath.string()); + if (!importer) + { + auto vtkReader = reader->createGeometryReader(filePath.string()); + // XXX: We assume the f3d reader provide either a scene reader or a geometry reader + // TODO: Put in f3d::reader directly + assert(vtkReader); + vtkSmartPointer genericImporter = + vtkSmartPointer::New(); + genericImporter->SetInternalReader(vtkReader); + importer = genericImporter; + } + importers.emplace_back(importer); } - // Read the file -#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) - if (!this->Internals->CurrentFullSceneImporter->Update()) + log::debug("\nLoading files: "); + if (filePaths.size() == 1) { - throw loader::load_failure_exception("failed to load scene: " + filePath); + log::debug(filePaths[0].string()); } -#else - this->Internals->CurrentFullSceneImporter->Update(); -#endif - - // Remove anything progress related if any - this->Internals->CurrentFullSceneImporter->RemoveObservers(vtkCommand::ProgressEvent); - progressWidget->Off(); - - // Initialize the animation using temporal information from the importer - if (this->Internals->AnimationManager.Initialize(&this->Internals->Options, - &this->Internals->Window, this->Internals->Interactor, - this->Internals->CurrentFullSceneImporter)) + else { - if (this->Internals->Options.scene.animation.time.has_value()) + for (const fs::path& filePathStr : filePaths) { - double animationTime = this->Internals->Options.scene.animation.time.value(); - double timeRange[2]; - this->Internals->AnimationManager.GetTimeRange(timeRange); - - // We assume importers import data at timeRange[0] when not specified - if (animationTime != timeRange[0]) - { - this->Internals->AnimationManager.LoadAtTime(animationTime); - } + log::debug("- ", filePathStr.string()); } } + log::debug(""); - // Set the name for animation - this->Internals->Window.setAnimationNameInfo( - this->Internals->AnimationManager.GetAnimationName()); - - // Display output description - loader_impl::internals::DisplayImporterDescription(this->Internals->CurrentFullSceneImporter); - - // Initialize renderer and reset camera to bounds if needed - this->Internals->Window.UpdateDynamicOptions(); - if (!this->Internals->Options.scene.camera.index.has_value()) - { - this->Internals->Window.getCamera().resetToBounds(); - } - - // Print info about scene and coloring - this->Internals->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); - this->Internals->Window.PrintSceneDescription(log::VerboseLevel::DEBUG); - + this->Internals->Load(importers); return *this; } //---------------------------------------------------------------------------- -loader& loader_impl::loadGeometry(const mesh_t& mesh, bool reset) +loader& loader_impl::add(const mesh_t& mesh) { // sanity checks auto [valid, err] = mesh.isValid(); @@ -377,39 +300,38 @@ loader& loader_impl::loadGeometry(const mesh_t& mesh, bool reset) vtkSource->SetNormals(mesh.normals); vtkSource->SetTCoords(mesh.texture_coordinates); vtkSource->SetFaces(mesh.face_sides, mesh.face_indices); - vtkSource->Update(); - this->Internals->LoadGeometry("", vtkSource, reset); + vtkSmartPointer importer = vtkSmartPointer::New(); + importer->SetInternalReader(vtkSource); + log::debug("Loading 3D scene from memory"); + this->Internals->Load({ importer }); return *this; } //---------------------------------------------------------------------------- -bool loader_impl::hasSceneReader(const std::string& filePath) +loader& loader_impl::clear() { - f3d::reader* reader = f3d::factory::instance()->getReader(filePath); - if (!reader) - { - return false; - } - return reader->hasSceneReader(); + // Clear the meta importer from all importers + this->Internals->MetaImporter->Clear(); + + // Clear the window of all actors + this->Internals->Window.Initialize(); + + return *this; } //---------------------------------------------------------------------------- -bool loader_impl::hasGeometryReader(const std::string& filePath) +bool loader_impl::supports(const fs::path& filePath) { - f3d::reader* reader = f3d::factory::instance()->getReader(filePath); - if (!reader) - { - return false; - } - return reader->hasGeometryReader(); + return f3d::factory::instance()->getReader(filePath.string()) != nullptr; } //---------------------------------------------------------------------------- void loader_impl::SetInteractor(interactor_impl* interactor) { this->Internals->Interactor = interactor; + this->Internals->AnimationManager.SetInteractor(interactor); this->Internals->Interactor->SetAnimationManager(&this->Internals->AnimationManager); } } diff --git a/library/src/window_impl.cxx b/library/src/window_impl.cxx index f70cf72c62..a8fa73bcdc 100644 --- a/library/src/window_impl.cxx +++ b/library/src/window_impl.cxx @@ -144,8 +144,6 @@ class window_impl::internals vtkNew Renderer; Type WindowType; const options& Options; - bool Initialized = false; - bool WithColoring = false; std::string CachePath; }; @@ -186,6 +184,21 @@ window_impl::window_impl(const options& options, Type type) this->Internals->RenWin->AddRenderer(this->Internals->Renderer); this->Internals->Camera = std::make_unique(); this->Internals->Camera->SetVTKRenderer(this->Internals->Renderer); + + this->Initialize(); + this->Internals->UpdateTheme(); +} + +//---------------------------------------------------------------------------- +void window_impl::Initialize() +{ + this->Internals->Renderer->Initialize(); +} + +//---------------------------------------------------------------------------- +void window_impl::InitializeUpVector() +{ + this->Internals->Renderer->InitializeUpVector(this->Internals->Options.scene.up_direction); } //---------------------------------------------------------------------------- @@ -197,13 +210,6 @@ window_impl::Type window_impl::getType() //---------------------------------------------------------------------------- camera& window_impl::getCamera() { - // Make sure the camera (and the whole rendering stack) - // is initialized before providing one. - if (!this->Internals->Initialized) - { - this->Initialize(false); - } - return *this->Internals->Camera; } @@ -307,24 +313,9 @@ window_impl::~window_impl() this->Internals->Renderer->ShowAxis(false); } -//---------------------------------------------------------------------------- -void window_impl::Initialize(bool withColoring) -{ - this->Internals->WithColoring = withColoring; - this->Internals->Renderer->Initialize(this->Internals->Options.scene.up_direction); - this->Internals->UpdateTheme(); - this->Internals->Initialized = true; -} - //---------------------------------------------------------------------------- void window_impl::UpdateDynamicOptions() { - if (!this->Internals->Initialized) - { - // Renderer is missing, create a default one - this->Initialize(false); - } - vtkF3DRendererWithColoring* renderer = this->Internals->Renderer; if (this->Internals->WindowType == Type::NONE) @@ -397,37 +388,27 @@ void window_impl::UpdateDynamicOptions() renderer->SetUseOrthographicProjection(opt.scene.camera.orthographic); } - if (this->Internals->WithColoring) - { - std::vector rgb = opt.model.color.rgb; - renderer->SetSurfaceColor(rgb.data()); - renderer->SetOpacity(opt.model.color.opacity); - renderer->SetTextureBaseColor(opt.model.color.texture); - renderer->SetRoughness(opt.model.material.roughness); - renderer->SetMetallic(opt.model.material.metallic); - renderer->SetTextureMaterial(opt.model.material.texture); - renderer->SetTextureEmissive(opt.model.emissive.texture); - std::vector factor = opt.model.emissive.factor; - renderer->SetEmissiveFactor(factor.data()); - renderer->SetTextureNormal(opt.model.normal.texture); - renderer->SetNormalScale(opt.model.normal.scale); - renderer->SetTextureMatCap(opt.model.matcap.texture); - - renderer->SetColoring(opt.model.scivis.enable, opt.model.scivis.cells, - opt.model.scivis.array_name, opt.model.scivis.component); - renderer->SetScalarBarRange(opt.model.scivis.range); - renderer->SetColormap(opt.model.scivis.colormap); - renderer->ShowScalarBar(opt.ui.scalar_bar); - - renderer->SetUsePointSprites(opt.model.point_sprites.enable); - renderer->SetUseVolume(opt.model.volume.enable); - renderer->SetUseInverseOpacityFunction(opt.model.volume.inverse); - } - else - { - // make sure the scalar bar is hidden without coloring - renderer->ShowScalarBar(false); - } + renderer->SetSurfaceColor(opt.model.color.rgb); + renderer->SetOpacity(opt.model.color.opacity); + renderer->SetTextureBaseColor(opt.model.color.texture); + renderer->SetRoughness(opt.model.material.roughness); + renderer->SetMetallic(opt.model.material.metallic); + renderer->SetTextureMaterial(opt.model.material.texture); + renderer->SetTextureEmissive(opt.model.emissive.texture); + renderer->SetEmissiveFactor(opt.model.emissive.factor); + renderer->SetTextureNormal(opt.model.normal.texture); + renderer->SetNormalScale(opt.model.normal.scale); + renderer->SetTextureMatCap(opt.model.matcap.texture); + + renderer->SetColoring(opt.model.scivis.enable, opt.model.scivis.cells, + opt.model.scivis.array_name, opt.model.scivis.component); + renderer->SetScalarBarRange(opt.model.scivis.range); + renderer->SetColormap(opt.model.scivis.colormap); + renderer->ShowScalarBar(opt.ui.scalar_bar); + + renderer->SetUsePointSprites(opt.model.point_sprites.enable); + renderer->SetUseVolume(opt.model.volume.enable); + renderer->SetUseInverseOpacityFunction(opt.model.volume.inverse); renderer->UpdateActors(); } @@ -441,13 +422,10 @@ void window_impl::PrintSceneDescription(log::VerboseLevel level) //---------------------------------------------------------------------------- void window_impl::PrintColoringDescription(log::VerboseLevel level) { - if (this->Internals->WithColoring) + std::string descr = this->Internals->Renderer->GetColoringDescription(); + if (!descr.empty()) { - std::string descr = this->Internals->Renderer->GetColoringDescription(); - if (!descr.empty()) - { - log::print(level, descr); - } + log::print(level, descr); } } @@ -495,12 +473,9 @@ image window_impl::renderToImage(bool noBackground) } //---------------------------------------------------------------------------- -void window_impl::SetImporterForColoring(vtkF3DGenericImporter* importer) +void window_impl::SetImporter(vtkF3DMetaImporter* importer) { - if (this->Internals->WithColoring) - { - this->Internals->Renderer->SetImporter(importer); - } + this->Internals->Renderer->SetImporter(importer); } //---------------------------------------------------------------------------- diff --git a/library/testing/TestSDKAnimation.cxx b/library/testing/TestSDKAnimation.cxx index 3c1fe45bee..3d6a209b14 100644 --- a/library/testing/TestSDKAnimation.cxx +++ b/library/testing/TestSDKAnimation.cxx @@ -9,7 +9,7 @@ int TestSDKAnimation(int argc, char* argv[]) f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); f3d::loader& load = eng.getLoader(); f3d::interactor& inter = eng.getInteractor(); - load.loadScene(std::string(argv[1]) + "/data/InterpolationTest.glb"); + load.add(std::string(argv[1]) + "/data/InterpolationTest.glb"); inter.startAnimation(); if (!inter.isPlayingAnimation()) diff --git a/library/testing/TestSDKCamera.cxx b/library/testing/TestSDKCamera.cxx index 3ef6fa1d7d..1325f8fce6 100644 --- a/library/testing/TestSDKCamera.cxx +++ b/library/testing/TestSDKCamera.cxx @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,7 @@ void checkDouble(const double actual, const double expected, const std::string& int TestSDKCamera(int argc, char* argv[]) { + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); f3d::window& win = eng.getWindow(); f3d::camera& cam = win.getCamera(); @@ -74,7 +76,6 @@ int TestSDKCamera(int argc, char* argv[]) // check coordinates conversion f3d::point3_t point = { 0.1, 0.1, 0.1 }; f3d::point3_t pointDC = win.getDisplayFromWorld(point); - if (!comparePoint(point, win.getWorldFromDisplay(pointDC))) { std::cerr << "coordinates conversion is not behaving as expected" << std::endl; diff --git a/library/testing/TestSDKCompareWithFile.cxx b/library/testing/TestSDKCompareWithFile.cxx index e44a4ecd8a..a1b9c63873 100644 --- a/library/testing/TestSDKCompareWithFile.cxx +++ b/library/testing/TestSDKCompareWithFile.cxx @@ -11,7 +11,7 @@ int TestSDKCompareWithFile(int argc, char* argv[]) f3d::window& win = eng.getWindow(); win.setSize(300, 300); - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + load.add(std::string(argv[1]) + "/data/cow.vtp"); return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", std::string(argv[2]), "TestSDKCompareWithFile", 50) diff --git a/library/testing/TestSDKDynamicBackgroundColor.cxx b/library/testing/TestSDKDynamicBackgroundColor.cxx index efafd3f7f1..49c406c724 100644 --- a/library/testing/TestSDKDynamicBackgroundColor.cxx +++ b/library/testing/TestSDKDynamicBackgroundColor.cxx @@ -15,7 +15,7 @@ int TestSDKDynamicBackgroundColor(int argc, char* argv[]) opt.ui.filename = true; opt.ui.filename_info = "(1/1) cow.vtp"; - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + load.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); diff --git a/library/testing/TestSDKDynamicFontFile.cxx b/library/testing/TestSDKDynamicFontFile.cxx index cb8c18dd6e..6178148ec5 100644 --- a/library/testing/TestSDKDynamicFontFile.cxx +++ b/library/testing/TestSDKDynamicFontFile.cxx @@ -15,7 +15,7 @@ int TestSDKDynamicFontFile(int argc, char* argv[]) opt.ui.filename = true; opt.ui.filename_info = "(1/1) cow.vtp"; - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + load.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); diff --git a/library/testing/TestSDKDynamicHDRI.cxx b/library/testing/TestSDKDynamicHDRI.cxx index e75a43c490..936d8bd56b 100644 --- a/library/testing/TestSDKDynamicHDRI.cxx +++ b/library/testing/TestSDKDynamicHDRI.cxx @@ -22,7 +22,7 @@ int TestSDKDynamicHDRI(int argc, char* argv[]) opt.ui.filename = true; opt.ui.filename_info = "(1/1) cow.vtp"; - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + load.add(std::string(argv[1]) + "/data/cow.vtp"); bool ret = win.render(); if (!ret) diff --git a/library/testing/TestSDKDynamicLightIntensity.cxx b/library/testing/TestSDKDynamicLightIntensity.cxx index bd9ac7e872..7ef6290b20 100644 --- a/library/testing/TestSDKDynamicLightIntensity.cxx +++ b/library/testing/TestSDKDynamicLightIntensity.cxx @@ -13,7 +13,7 @@ int TestSDKDynamicLightIntensity(int argc, char* argv[]) f3d::options& opt = eng.getOptions(); win.setSize(300, 300); - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + load.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); diff --git a/library/testing/TestSDKDynamicProperties.cxx b/library/testing/TestSDKDynamicProperties.cxx index 1759267d06..0da8587e68 100644 --- a/library/testing/TestSDKDynamicProperties.cxx +++ b/library/testing/TestSDKDynamicProperties.cxx @@ -15,7 +15,7 @@ int TestSDKDynamicProperties(int argc, char* argv[]) opt.ui.filename = true; opt.ui.filename_info = "(1/1) cow.vtp"; - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + load.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); diff --git a/library/testing/TestSDKExternalWindowGLFW.cxx b/library/testing/TestSDKExternalWindowGLFW.cxx index dafe686cde..6f1896b6c6 100644 --- a/library/testing/TestSDKExternalWindowGLFW.cxx +++ b/library/testing/TestSDKExternalWindowGLFW.cxx @@ -12,7 +12,7 @@ int TestSDKExternalWindowGLFW(int argc, char* argv[]) { // create engine and load file f3d::engine eng(f3d::window::Type::EXTERNAL); - eng.getLoader().loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + eng.getLoader().add(std::string(argv[1]) + "/data/cow.vtp"); // setup glfw window if (!glfwInit()) diff --git a/library/testing/TestSDKExternalWindowQT.cxx b/library/testing/TestSDKExternalWindowQT.cxx index f32e9755e5..7ae6a0facb 100644 --- a/library/testing/TestSDKExternalWindowQT.cxx +++ b/library/testing/TestSDKExternalWindowQT.cxx @@ -20,7 +20,7 @@ class F3DWindow : public QOpenGLWindow , mOutputPath(std::move(outputPath)) { f3d::loader& load = mEngine.getLoader(); - load.loadGeometry(filePath); + load.add(filePath); } protected: diff --git a/library/testing/TestSDKInteractorCallBack.cxx b/library/testing/TestSDKInteractorCallBack.cxx index 21ed9b85b8..bc4b714cf6 100644 --- a/library/testing/TestSDKInteractorCallBack.cxx +++ b/library/testing/TestSDKInteractorCallBack.cxx @@ -49,7 +49,7 @@ int TestSDKInteractorCallBack(int argc, char* argv[]) inter.setDropFilesCallBack([&](std::vector filesVec) -> bool { std::string path = filesVec[0]; size_t found = path.find_last_of("/\\"); - load.loadGeometry(path.substr(0, found + 1) + "suzanne.ply", true); + load.add(path.substr(0, found + 1) + "suzanne.ply"); win.render(); return true; }); diff --git a/library/testing/TestSDKLoadFromMemory.cxx b/library/testing/TestSDKLoadFromMemory.cxx index 04d55899da..eadd47e5c3 100644 --- a/library/testing/TestSDKLoadFromMemory.cxx +++ b/library/testing/TestSDKLoadFromMemory.cxx @@ -1,110 +1,75 @@ +#include "PseudoUnitTest.h" +#include "TestSDKHelpers.h" + #include #include #include #include -#include "TestSDKHelpers.h" - #include int TestSDKLoadFromMemory(int argc, char* argv[]) { + PseudoUnitTest test; + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); + f3d::loader& load = eng.getLoader(); f3d::window& win = eng.getWindow().setSize(300, 300); std::string texturePath = std::string(argv[1]) + "data/world.png"; eng.getOptions().model.color.texture = texturePath; - // Load invalid number of points - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f }, {}, {}, { 3 }, { 0, 1, 2 } }); - std::cerr << "Should throw: invalid number of points" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } + // Add mesh with invalid number of points + test.expect("add mesh with invalid number of points", [&]() { + load.add(f3d::mesh_t{ { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f }, {}, {}, { 3 }, { 0, 1, 2 } }); + }); - // Load empty point - try - { - eng.getLoader().loadGeometry(f3d::mesh_t{}); - std::cerr << "Should throw: no point" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } + // Add mesh with empty points + test.expect( + "add mesh with empty points", [&]() { load.add(f3d::mesh_t{}); }); - // Load invalid number of points - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, {}, { 3 }, { 0, 1, 2, 3 } }); - std::cerr << "Should throw: invalid number of cell indices" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } + // Add mesh with invalid number of cell indices + test.expect( + "add mesh with invalid number of cell indices", [&]() { + load.add(f3d::mesh_t{ + { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, {}, { 3 }, { 0, 1, 2, 3 } }); + }); - // Load invalid with invalid index - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, {}, { 3 }, { 0, 1, 4 } }); - std::cerr << "Should throw: invalid vertex index" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } + // Add mesh with invalid vertex index + test.expect("add mesh with invalid vertex index", [&]() { + load.add( + f3d::mesh_t{ { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, {}, { 3 }, { 0, 1, 4 } }); + }); - // Load invalid with invalid normals - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, { 1.f }, {}, { 3 }, { 0, 1, 2, 4 } }); - std::cerr << "Should throw: invalid normals" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } + // Add mesh with invalid normals + test.expect("add mesh with invalid normals", [&]() { + load.add(f3d::mesh_t{ + { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, { 1.f }, {}, { 3 }, { 0, 1, 2, 4 } }); + }); - // Load invalid with invalid texture coordinates - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, { 1.f }, { 3 }, { 0, 1, 2, 4 } }); - std::cerr << "Should throw: invalid texture coordinates" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } + // Add mesh with invalid texture coordinates + test.expect( + "add mesh with invalid texture coordinates", [&]() { + load.add(f3d::mesh_t{ + { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, { 1.f }, { 3 }, { 0, 1, 2, 4 } }); + }); - // Load from memory (valid) - try - { - eng.getLoader().loadGeometry({ { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 0.f }, + // Add mesh from memory and render it + test("add mesh from memory", [&]() { + load.add(f3d::mesh_t{ { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 0.f }, { 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f }, { 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f }, { 3, 3 }, { 0, 1, 2, 1, 3, 2 } }); - } - catch (const f3d::loader::load_failure_exception& ex) - { - std::cerr << "Unexpected loadMemory failure" << std::endl; - return EXIT_FAILURE; - } + }); - if (!TestSDKHelpers::RenderTest( - win, std::string(argv[1]) + "baselines/", argv[2], "TestSDKLoadMemory")) - { - return EXIT_FAILURE; - } + // Render test + test("render mesh from memory", [&]() { + if (!TestSDKHelpers::RenderTest( + win, std::string(argv[1]) + "baselines/", argv[2], "TestSDKLoadMemory")) + { + throw "rendering test failed"; + } + }); - return EXIT_SUCCESS; + return test.result(); } diff --git a/library/testing/TestSDKLoader.cxx b/library/testing/TestSDKLoader.cxx index bd814db4cb..cf15193ee7 100644 --- a/library/testing/TestSDKLoader.cxx +++ b/library/testing/TestSDKLoader.cxx @@ -1,3 +1,6 @@ +#include "PseudoUnitTest.h" +#include "TestSDKHelpers.h" + #include #include #include @@ -6,180 +9,66 @@ #include +namespace fs = std::filesystem; + int TestSDKLoader(int argc, char* argv[]) { + PseudoUnitTest test; + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); - f3d::engine eng(f3d::window::Type::NONE); + f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); f3d::loader& load = eng.getLoader(); + f3d::window& win = eng.getWindow().setSize(300, 300); // Test file logic std::string empty; std::string dummyFilename = "dummy.foo"; - std::string nonExistentGeometryFilename = "nonExistent.vtp"; - std::string nonExistentFullSceneFilename = "nonExistent.obj"; + std::string nonExistentFilename = "nonExistent.vtp"; std::string unsupportedFilename = "unsupportedFile.dummy"; - std::string cowFilename = "cow.vtp"; - std::string dragonFilename = "dragon.vtu"; - std::string suzanneFilename = "suzanne.stl"; + std::string logoFilename = "mb/recursive/f3d.glb"; + std::string sphere1Filename = "mb/recursive/mb_1_0.vtp"; + std::string sphere2Filename = "mb/recursive/mb_2_0.vtp"; + std::string cubeFilename = "mb/recursive/mb_0_0.vtu"; std::string worldFilename = "world.obj"; - std::string botFilename = "bot2.wrl"; std::string dummy = std::string(argv[1]) + "data/" + dummyFilename; - std::string nonExistentGeometry = std::string(argv[1]) + "data/" + nonExistentGeometryFilename; - std::string nonExistentFullScene = std::string(argv[1]) + "data/" + nonExistentFullSceneFilename; + std::string nonExistent = std::string(argv[1]) + "data/" + nonExistentFilename; std::string unsupported = std::string(argv[1]) + "data/" + unsupportedFilename; - std::string cow = std::string(argv[1]) + "data/" + cowFilename; - std::string dragon = std::string(argv[1]) + "data/" + dragonFilename; - std::string suzanne = std::string(argv[1]) + "data/" + suzanneFilename; + std::string logo = std::string(argv[1]) + "data/" + logoFilename; + std::string sphere1 = std::string(argv[1]) + "data/" + sphere1Filename; + std::string sphere2 = std::string(argv[1]) + "data/" + sphere2Filename; + std::string cube = std::string(argv[1]) + "data/" + cubeFilename; std::string world = std::string(argv[1]) + "data/" + worldFilename; - std::string bot = std::string(argv[1]) + "data/" + botFilename; - - // has*Reader methods - if (load.hasGeometryReader(empty) || load.hasSceneReader(empty)) - { - std::cerr << "Unexpected has*Reader output with empty filenames" << std::endl; - return EXIT_FAILURE; - } - if (load.hasGeometryReader(dummy) || load.hasSceneReader(dummy)) - { - std::cerr << "Unexpected has*Reader output with dummy filenames" << std::endl; - return EXIT_FAILURE; - } - if (!load.hasGeometryReader(nonExistentGeometry) || !load.hasSceneReader(nonExistentFullScene)) - { - std::cerr << "Unexpected has*Reader output with non existent filenames" << std::endl; - return EXIT_FAILURE; - } - if (load.hasGeometryReader(bot) || load.hasSceneReader(dragon)) - { - std::cerr << "Unexpected has*Reader output with incorrect formats" << std::endl; - return EXIT_FAILURE; - } - if (!load.hasGeometryReader(dragon) || !load.hasSceneReader(bot)) - { - std::cerr << "Unexpected has*Reader output with correct formats" << std::endl; - return EXIT_FAILURE; - } - if (!load.hasGeometryReader(world) || !load.hasSceneReader(world)) - { - std::cerr << "Unexpected has*Reader output with geometry and full scene format" << std::endl; - return EXIT_FAILURE; - } - - // Empty filename, success expected but nothing is loaded - try - { - load.loadGeometry(empty); - } - catch (const f3d::loader::load_failure_exception& ex) - { - std::cerr << "Unexpected loadGeometry failure with an empty file" << std::endl; - return EXIT_FAILURE; - } - - try - { - load.loadScene(empty); - } - catch (const f3d::loader::load_failure_exception& ex) - { - std::cerr << "Unexpected loadGeometry failure with an empty file" << std::endl; - return EXIT_FAILURE; - } - - // Dummy filename - try - { - load.loadGeometry(dummy); - std::cerr << "Unexpected loadGeometry success with a dummy file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(dummy); - std::cerr << "Unexpected loadGeometry success with a dummy file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Non supported files - try - { - load.loadGeometry(unsupported); - std::cerr << "Unexpected loadGeometry success with an unsupported file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(unsupported); - std::cerr << "Unexpected loadScene success with an unsupported file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Incorrect files - try - { - load.loadGeometry(bot); - std::cerr << "Unexpected loadGeometry success with an incorrect file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(cow); - std::cerr << "Unexpected loadScene success with an incorrect file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Non existent files - try - { - load.loadGeometry(nonExistentGeometry); - std::cerr << "Unexpected loadGeometry success with a non existent file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(nonExistentFullScene); - std::cerr << "Unexpected loadScene success with a non existent file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Multiple geometries - try - { - load.loadGeometry(cow).loadGeometry(suzanne).loadGeometry(dragon); - } - catch (const f3d::loader::load_failure_exception& ex) - { - std::cerr << "Unexpected loadGeometry failure with multiple files" << std::endl; - return EXIT_FAILURE; - } - return EXIT_SUCCESS; + // supports method + test("supported with empty filename", !load.supports(empty)); + test("supported with dummy filename", !load.supports(dummy)); + test("supported with non existent filename", load.supports(nonExistent)); + test("supported with default scene format", load.supports(cube)); + test("supported with full scene format", load.supports(logo)); + + // add error code paths + test.expect( + "add with dummy file", [&]() { load.add(dummy); }); + test.expect( + "add with unsupported file", [&]() { load.add(unsupported); }); + test.expect( + "add with inexistent file", [&]() { load.add(nonExistent); }); + + // add standard code paths + test("add with empty file", [&]() { load.add(std::vector{}); }); + test("add with empty file", [&]() { load.add(empty); }); + test("add with a single path", [&]() { load.add(fs::path(logo)); }); + test("add with multiples filepaths", [&]() { load.add({ fs::path(sphere2), fs::path(cube) }); }); + test("add with multiples file strings", [&]() { load.add({ sphere1, world }); }); + + // render test + test("render after add", [&]() { + if (!TestSDKHelpers::RenderTest( + win, std::string(argv[1]) + "baselines/", argv[2], "TestSDKLoader")) + { + throw "rendering test failed"; + } + }); + + return test.result(); } diff --git a/library/testing/TestSDKLoaderInvalid.cxx b/library/testing/TestSDKLoaderInvalid.cxx index 073e089eb3..01fb40c91b 100644 --- a/library/testing/TestSDKLoaderInvalid.cxx +++ b/library/testing/TestSDKLoaderInvalid.cxx @@ -1,3 +1,5 @@ +#include "PseudoUnitTest.h" + #include #include #include @@ -8,34 +10,21 @@ int TestSDKLoaderInvalid(int argc, char* argv[]) { + PseudoUnitTest test; + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); f3d::engine eng(f3d::window::Type::NONE); f3d::loader& load = eng.getLoader(); - std::string invalidGeometryFilename = "invalid.vtp"; + std::string invalidDefaultSceneFilename = "invalid.vtp"; std::string invalidFullSceneFilename = "duck_invalid.gltf"; - std::string invalidGeometry = std::string(argv[1]) + "data/" + invalidGeometryFilename; + std::string invalidDefaultScene = std::string(argv[1]) + "data/" + invalidDefaultSceneFilename; std::string invalidFullScene = std::string(argv[1]) + "data/" + invalidFullSceneFilename; - try - { - load.loadGeometry(invalidGeometry); - std::cerr << "Unexpected loadGeometry success with an invalid file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(invalidFullScene); - std::cerr << "Unexpected loadScene success with an invalid file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } + test.expect( + "add with invalid default scene file", [&]() { load.add(invalidDefaultScene); }); + test.expect( + "add with invalid full scene file", [&]() { load.add(invalidFullScene); }); - return EXIT_SUCCESS; + return test.result(); } diff --git a/library/testing/TestSDKMultiColoring.cxx b/library/testing/TestSDKMultiColoring.cxx index 921e35fbd2..93c51a7fa2 100644 --- a/library/testing/TestSDKMultiColoring.cxx +++ b/library/testing/TestSDKMultiColoring.cxx @@ -24,7 +24,7 @@ int TestSDKMultiColoring(int argc, char* argv[]) std::string right = std::string(argv[1]) + "data/" + rightFilename; // Multiple geometries - load.loadGeometry(cube).loadGeometry(left).loadGeometry(right); + load.add(std::vector{ cube, left, right }); opt.model.scivis.array_name = "Normals"; diff --git a/library/testing/TestSDKMultiOptions.cxx b/library/testing/TestSDKMultiOptions.cxx index e669866078..9fcbe64fc8 100644 --- a/library/testing/TestSDKMultiOptions.cxx +++ b/library/testing/TestSDKMultiOptions.cxx @@ -22,7 +22,7 @@ int TestSDKMultiOptions(int argc, char* argv[]) std::string right = std::string(argv[1]) + "data/" + rightFilename; // Render one geometry with a render option - load.loadGeometry(left); + load.add(left); opt.render.show_edges = true; opt.render.grid.enable = true; opt.ui.metadata = true; @@ -30,7 +30,7 @@ int TestSDKMultiOptions(int argc, char* argv[]) win.render(); // Add another geometry - load.loadGeometry(right); + load.add(right); // Check rendering is correct return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", diff --git a/library/testing/TestSDKOptions.cxx b/library/testing/TestSDKOptions.cxx index b0e76a8fa9..06f4236d5b 100644 --- a/library/testing/TestSDKOptions.cxx +++ b/library/testing/TestSDKOptions.cxx @@ -1,8 +1,8 @@ +#include "PseudoUnitTest.h" + #include #include -#include "PseudoUnitTest.h" - #include #include @@ -214,9 +214,9 @@ int TestSDKOptions(int argc, char* argv[]) f3d::options opt7{}; // Test reset non-optional values - opt7.model.color.opacity = 0.5; - opt7.reset("model.color.opacity"); - test("reset non-optional values", opt7.model.color.opacity == 1.0); + opt7.scene.up_direction = "+Z"; + opt7.reset("scene.up_direction"); + test("reset non-optional values", opt7.scene.up_direction == "+Y"); // Test reset optional values opt7.model.scivis.array_name = "dummy"; diff --git a/library/testing/TestSDKRenderAndInteract.cxx b/library/testing/TestSDKRenderAndInteract.cxx index 0d8ba9e19a..61c459f342 100644 --- a/library/testing/TestSDKRenderAndInteract.cxx +++ b/library/testing/TestSDKRenderAndInteract.cxx @@ -12,7 +12,7 @@ int TestSDKRenderAndInteract(int argc, char* argv[]) f3d::engine eng(f3d::window::Type::NATIVE); f3d::loader& load = eng.getLoader(); - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + load.add(std::string(argv[1]) + "/data/cow.vtp"); f3d::window& win = eng.getWindow(); win.render(); diff --git a/library/testing/TestSDKRenderFinalShader.cxx b/library/testing/TestSDKRenderFinalShader.cxx index f767a3df67..30481a7e42 100644 --- a/library/testing/TestSDKRenderFinalShader.cxx +++ b/library/testing/TestSDKRenderFinalShader.cxx @@ -13,7 +13,7 @@ int TestSDKRenderFinalShader(int argc, char* argv[]) win.setSize(300, 300); f3d::loader& load = eng.getLoader(); - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + load.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); diff --git a/plugins/alembic/configs/config.d/10_alembic.json b/plugins/alembic/configs/config.d/10_alembic.json index 6ad25f6d43..286f47fe09 100644 --- a/plugins/alembic/configs/config.d/10_alembic.json +++ b/plugins/alembic/configs/config.d/10_alembic.json @@ -1,6 +1,7 @@ { ".*(abc)": { + "scalar-coloring": true, "load-plugins": "alembic" } } diff --git a/plugins/alembic/configs/thumbnail.d/10_alembic.json b/plugins/alembic/configs/thumbnail.d/10_alembic.json index 6ad25f6d43..286f47fe09 100644 --- a/plugins/alembic/configs/thumbnail.d/10_alembic.json +++ b/plugins/alembic/configs/thumbnail.d/10_alembic.json @@ -1,6 +1,7 @@ { ".*(abc)": { + "scalar-coloring": true, "load-plugins": "alembic" } } diff --git a/plugins/draco/CMakeLists.txt b/plugins/draco/CMakeLists.txt index e824391367..4f9b326fdf 100644 --- a/plugins/draco/CMakeLists.txt +++ b/plugins/draco/CMakeLists.txt @@ -32,10 +32,8 @@ if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240214) SCORE 90 EXTENSIONS gltf glb MIMETYPES model/gltf-binary model/gltf+json - VTK_READER vtkF3DGLTFReader VTK_IMPORTER vtkF3DGLTFImporter FORMAT_DESCRIPTION "GL Transmission Format" - CUSTOM_CODE "${CMAKE_CURRENT_SOURCE_DIR}/gltf.inl" ) endif() diff --git a/plugins/draco/configs/config.d/10_draco.json b/plugins/draco/configs/config.d/10_draco.json index 041998db54..8428b17b2a 100644 --- a/plugins/draco/configs/config.d/10_draco.json +++ b/plugins/draco/configs/config.d/10_draco.json @@ -1,6 +1,7 @@ { ".*(drc)": { + "scalar-coloring": true, "load-plugins": "draco" } } diff --git a/plugins/draco/configs/thumbnail.d/10_draco.json b/plugins/draco/configs/thumbnail.d/10_draco.json index 041998db54..8428b17b2a 100644 --- a/plugins/draco/configs/thumbnail.d/10_draco.json +++ b/plugins/draco/configs/thumbnail.d/10_draco.json @@ -1,6 +1,7 @@ { ".*(drc)": { + "scalar-coloring": true, "load-plugins": "draco" } } diff --git a/plugins/draco/gltf.inl b/plugins/draco/gltf.inl deleted file mode 100644 index 1d7a52a6aa..0000000000 --- a/plugins/draco/gltf.inl +++ /dev/null @@ -1,17 +0,0 @@ -void applyCustomReader(vtkAlgorithm* algo, const std::string&) const override -{ - vtkGLTFReader* gltfReader = vtkGLTFReader::SafeDownCast(algo); - - // Enable all animations in the GLTFReader - // Specifying a non-zero framerate in the next call is not needed after VTK 9.2.20230603 : VTK_VERSION_CHECK(9, 2, 20230603) - gltfReader->SetFrameRate(30); - gltfReader->ApplyDeformationsToGeometryOn(); - gltfReader->UpdateInformation(); // Read model metadata to get the number of animations - for (vtkIdType i = 0; i < gltfReader->GetNumberOfAnimations(); i++) - { - gltfReader->EnableAnimation(i); - } - // It is needed to update the information directly in order to recover it later - // Not entirely understood, TODO - gltfReader->UpdateInformation(); -} diff --git a/plugins/draco/module/CMakeLists.txt b/plugins/draco/module/CMakeLists.txt index 717848ad94..e9de29edf3 100644 --- a/plugins/draco/module/CMakeLists.txt +++ b/plugins/draco/module/CMakeLists.txt @@ -7,7 +7,6 @@ if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240214) set(classes ${classes} vtkF3DGLTFDocumentLoader vtkF3DGLTFImporter - vtkF3DGLTFReader ) endif() diff --git a/plugins/draco/module/vtkF3DGLTFReader.cxx b/plugins/draco/module/vtkF3DGLTFReader.cxx deleted file mode 100644 index 88dd6e019b..0000000000 --- a/plugins/draco/module/vtkF3DGLTFReader.cxx +++ /dev/null @@ -1,14 +0,0 @@ -#include "vtkF3DGLTFReader.h" - -#include "vtkF3DGLTFDocumentLoader.h" - -#include - -//---------------------------------------------------------------------------- -vtkStandardNewMacro(vtkF3DGLTFReader); - -//---------------------------------------------------------------------------- -void vtkF3DGLTFReader::InitializeLoader() -{ - this->Loader = vtkSmartPointer::New(); -} diff --git a/plugins/draco/module/vtkF3DGLTFReader.h b/plugins/draco/module/vtkF3DGLTFReader.h deleted file mode 100644 index 166ddd2851..0000000000 --- a/plugins/draco/module/vtkF3DGLTFReader.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @class vtkF3DGLTFReader - * @brief VTK GLTF reader with Draco support - * - * Subclasses the native importer to initialize our own loader. - * @sa vtkF3DGLTFDocumentLoader - */ - -#ifndef vtkF3DGLTFReader_h -#define vtkF3DGLTFReader_h - -#include - -class vtkF3DGLTFReader : public vtkGLTFReader -{ -public: - static vtkF3DGLTFReader* New(); - vtkTypeMacro(vtkF3DGLTFReader, vtkGLTFReader); - -protected: - vtkF3DGLTFReader() = default; - ~vtkF3DGLTFReader() override = default; - - /** - * Overridden to instantiate our own document loader - */ - void InitializeLoader() override; - -private: - vtkF3DGLTFReader(const vtkF3DGLTFReader&) = delete; - void operator=(const vtkF3DGLTFReader&) = delete; -}; - -#endif diff --git a/plugins/exodus/configs/config.d/10_exodus.json b/plugins/exodus/configs/config.d/10_exodus.json index db032e9584..4356640057 100644 --- a/plugins/exodus/configs/config.d/10_exodus.json +++ b/plugins/exodus/configs/config.d/10_exodus.json @@ -1,6 +1,7 @@ { ".*(ex2)": { + "scalar-coloring": true, "load-plugins": "exodus", "bar": true } diff --git a/plugins/exodus/configs/thumbnail.d/10_exodus.json b/plugins/exodus/configs/thumbnail.d/10_exodus.json index 375b1c7498..a9881e23ce 100644 --- a/plugins/exodus/configs/thumbnail.d/10_exodus.json +++ b/plugins/exodus/configs/thumbnail.d/10_exodus.json @@ -1,6 +1,7 @@ { ".*(ex2)": { + "scalar-coloring": true, "load-plugins": "exodus" } } diff --git a/plugins/native/CMakeLists.txt b/plugins/native/CMakeLists.txt index 14c3e39c43..15de60c48d 100644 --- a/plugins/native/CMakeLists.txt +++ b/plugins/native/CMakeLists.txt @@ -42,10 +42,8 @@ f3d_plugin_declare_reader( SCORE 80 EXTENSIONS gltf glb MIMETYPES model/gltf-binary model/gltf+json - VTK_READER vtkGLTFReader VTK_IMPORTER vtkGLTFImporter FORMAT_DESCRIPTION "GL Transmission Format" - CUSTOM_CODE "${CMAKE_CURRENT_SOURCE_DIR}/gltf.inl" ) f3d_plugin_declare_reader( @@ -68,7 +66,6 @@ f3d_plugin_declare_reader( NAME OBJ EXTENSIONS obj MIMETYPES model/obj - VTK_READER vtkOBJReader VTK_IMPORTER vtkOBJImporter FORMAT_DESCRIPTION "Wavefront OBJ" CUSTOM_CODE "${CMAKE_CURRENT_SOURCE_DIR}/obj.inl" diff --git a/plugins/native/configs/config.d/10_native.json b/plugins/native/configs/config.d/10_native.json index 1942c9a078..46be768c6a 100644 --- a/plugins/native/configs/config.d/10_native.json +++ b/plugins/native/configs/config.d/10_native.json @@ -1,9 +1,10 @@ { - ".*(dcm|nrrd|mhd|mha|vt.)": + ".*(dcm|nrrd|nhdr|mhd|mha|vt.)": { + "scalar-coloring": true, "bar": true }, - ".*(dcm|nrrd|mhd|mha|vti)": + ".*(dcm|nrrd|nhdr|mhd|mha|vti)": { "volume": true }, @@ -12,14 +13,20 @@ "up": "+Z", "camera-direction": "-1,1,-0.5" }, + ".*(stl|gml|pts)": + { + "scalar-coloring": true + }, ".*(tif|tiff)": { + "scalar-coloring": true, "up": "-Y", "comp": "-2", "camera-direction": "-1,0.5,1" }, ".*(ply)": { + "scalar-coloring": true, "comp": "-2", "translucency-support": true }, diff --git a/plugins/native/configs/thumbnail.d/10_native.json b/plugins/native/configs/thumbnail.d/10_native.json index 90d2239d6b..4e1fba656e 100644 --- a/plugins/native/configs/thumbnail.d/10_native.json +++ b/plugins/native/configs/thumbnail.d/10_native.json @@ -1,6 +1,7 @@ { ".*(dcm|nrrd|mhd|mha|vti)": { + "scalar-coloring": true, "volume": true }, ".*(3ds|stl)": @@ -8,8 +9,13 @@ "up": "+Z", "camera-direction": "-1,1,-0.5" }, + ".*(stl|gml|pts|vt.)": + { + "scalar-coloring": true + }, ".*(ply)": { + "scalar-coloring": true, "comp": "-2", "translucency-support": true }, diff --git a/plugins/native/gltf.inl b/plugins/native/gltf.inl deleted file mode 100644 index 1d7a52a6aa..0000000000 --- a/plugins/native/gltf.inl +++ /dev/null @@ -1,17 +0,0 @@ -void applyCustomReader(vtkAlgorithm* algo, const std::string&) const override -{ - vtkGLTFReader* gltfReader = vtkGLTFReader::SafeDownCast(algo); - - // Enable all animations in the GLTFReader - // Specifying a non-zero framerate in the next call is not needed after VTK 9.2.20230603 : VTK_VERSION_CHECK(9, 2, 20230603) - gltfReader->SetFrameRate(30); - gltfReader->ApplyDeformationsToGeometryOn(); - gltfReader->UpdateInformation(); // Read model metadata to get the number of animations - for (vtkIdType i = 0; i < gltfReader->GetNumberOfAnimations(); i++) - { - gltfReader->EnableAnimation(i); - } - // It is needed to update the information directly in order to recover it later - // Not entirely understood, TODO - gltfReader->UpdateInformation(); -} diff --git a/plugins/occt/configs/config.d/10_occt.json b/plugins/occt/configs/config.d/10_occt.json index 5d426efd0b..f14624bd71 100644 --- a/plugins/occt/configs/config.d/10_occt.json +++ b/plugins/occt/configs/config.d/10_occt.json @@ -1,6 +1,7 @@ { ".*(step|stp|iges|igs|brep|xbf)": { + "scalar-coloring": true, "load-plugins": "occt", "up": "+Z", "ambient-occlusion": true, diff --git a/plugins/occt/configs/thumbnail.d/10_occt.json b/plugins/occt/configs/thumbnail.d/10_occt.json index 5d426efd0b..f14624bd71 100644 --- a/plugins/occt/configs/thumbnail.d/10_occt.json +++ b/plugins/occt/configs/thumbnail.d/10_occt.json @@ -1,6 +1,7 @@ { ".*(step|stp|iges|igs|brep|xbf)": { + "scalar-coloring": true, "load-plugins": "occt", "up": "+Z", "ambient-occlusion": true, diff --git a/python/F3DPythonBindings.cxx b/python/F3DPythonBindings.cxx index 3c915d3b25..4fbb2a07c7 100644 --- a/python/F3DPythonBindings.cxx +++ b/python/F3DPythonBindings.cxx @@ -1,6 +1,7 @@ #include #include #include +#include #include "camera.h" #include "engine.h" @@ -242,13 +243,16 @@ PYBIND11_MODULE(pyf3d, module) // f3d::loader py::class_> loader(module, "Loader"); loader // - .def("has_geometry_reader", &f3d::loader::hasGeometryReader) - .def("load_geometry", py::overload_cast(&f3d::loader::loadGeometry), - "load geometry to a default scene", py::arg("file_path"), py::arg("reset") = false) - .def("has_scene_reader", &f3d::loader::hasSceneReader) - .def("load_scene", &f3d::loader::loadScene, "Load a specific full scene file") - .def("load_geometry", py::overload_cast(&f3d::loader::loadGeometry), - "Load a surfacic mesh from memory", py::arg("mesh"), py::arg("reset") = false); + .def("supports", &f3d::loader::supports) + .def("clear", &f3d::loader::clear) + .def("add", py::overload_cast(&f3d::loader::add), + "Add a file the scene", py::arg("file_path")) + .def("add", py::overload_cast&>(&f3d::loader::add), + "Add multiple filepaths to the scene", py::arg("file_path_vector")) + .def("add", py::overload_cast&>(&f3d::loader::add), + "Add multiple filenames to the scene", py::arg("file_name_vector")) + .def("add", py::overload_cast(&f3d::loader::add), + "Add a surfacic mesh from memory into the scene", py::arg("mesh")); // f3d::camera py::class_> camera(module, "Camera"); diff --git a/python/testing/test_image_compare.py b/python/testing/test_image_compare.py index 65953b0382..547c5d431e 100644 --- a/python/testing/test_image_compare.py +++ b/python/testing/test_image_compare.py @@ -19,7 +19,7 @@ def test_compare_with_file(): assert engine.window.width == 300 assert engine.window.height == 300 - engine.loader.load_geometry(dataset, True) + engine.loader.add(dataset) img = engine.window.render_to_image() img.save(output) diff --git a/python/testing/test_loader.py b/python/testing/test_loader.py index 8069614efd..b92d78a4fa 100644 --- a/python/testing/test_loader.py +++ b/python/testing/test_loader.py @@ -9,12 +9,11 @@ def test_load_memory(): testing_dir = Path(__file__).parent.parent.parent / "testing" reference = f"{testing_dir}/baselines/TestPythonLoadMemory.png" output = tempfile.gettempdir() + "/TestPythonLoadMemory.png" - outputDiff = tempfile.gettempdir() + "/TestPythonLoadMemory.diff.png" engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) engine.window.size = 300, 300 - engine.loader.load_geometry( + engine.loader.add( f3d.Mesh( points=[0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0], face_sides=[3], @@ -28,3 +27,29 @@ def test_load_memory(): error = 0.0 assert img.compare(f3d.Image(reference), 0.05, error) + + +def test_loader(): + testing_dir = Path(__file__).parent.parent.parent / "testing" + world = f"{testing_dir}/data/world.obj" + logo = f"{testing_dir}/data/f3d.glb" + sphere1 = f"{testing_dir}/data/mb/recursive/mb_1_0.vtp" + sphere2 = f"{testing_dir}/data/mb/recursive/mb_2_0.vtp" + cube = f"{testing_dir}/data/mb/recursive/mb_0_0.vtu" + cube = f"{testing_dir}/data/f3d.glb" + reference = f"{testing_dir}/baselines/TestPythonLoader.png" + output = tempfile.gettempdir() + "/TestPythonLoader.png" + + engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine.window.size = 300, 300 + + engine.loader.add([world, logo]) + engine.loader.add(Path(sphere1)) + engine.loader.add([Path(sphere2), Path(cube)]) + + img = engine.window.render_to_image() + img.save(output) + + error = 0.0 + + assert img.compare(f3d.Image(reference), 0.05, error) diff --git a/python/testing/test_options.py b/python/testing/test_options.py index ea381848af..d3c72221a2 100644 --- a/python/testing/test_options.py +++ b/python/testing/test_options.py @@ -12,10 +12,10 @@ def test_closest_option(): def test_setitem(): options = f3d.Options() options["interactor.axis"] = False - options["model.material.roughness"] = 0.3 + options["scene.animation.frame_rate"] = 33.33 options["scene.animation.speed_factor"] = 3.3 options["render.raytracing.samples"] = 5 - options["model.color.rgb"] = [1.0, 1.0, 1.0] + options["render.grid.color"] = [1.0, 1.0, 1.0] options["scene.up_direction"] = "+Y" @@ -24,10 +24,10 @@ def test_getitem(): options = engine.options assert options["interactor.axis"] is False - assert options["model.material.roughness"] == 0.3 + assert options["scene.animation.frame_rate"] == 60.0 assert options["scene.animation.speed_factor"] == 1.0 assert options["render.raytracing.samples"] == 5 - assert options["model.color.rgb"] == [1.0, 1.0, 1.0] + assert options["render.grid.color"] == [0.0, 0.0, 0.0] assert options["scene.up_direction"] == "+Y" @@ -61,7 +61,7 @@ def test_iter(): def test_contains(): options = f3d.Options() - assert "model.color.rgb" in options + assert "render.grid.color" in options assert "hello.world" not in options diff --git a/resources/configs/config.d/05_all.json b/resources/configs/config.d/05_all.json index 5a3c4bbc75..863318b5e9 100644 --- a/resources/configs/config.d/05_all.json +++ b/resources/configs/config.d/05_all.json @@ -7,7 +7,6 @@ "progress": true, "anti-aliasing": true, "filename": true, - "scalar-coloring": true, "camera-direction": "-1,-0.5,-1", "hdri-ambient": true, "translucency-support": true, diff --git a/resources/configs/thumbnail.d/05_all.json b/resources/configs/thumbnail.d/05_all.json index 7c337897e7..edf80c7da0 100644 --- a/resources/configs/thumbnail.d/05_all.json +++ b/resources/configs/thumbnail.d/05_all.json @@ -7,7 +7,6 @@ "max-size":100, "no-background": true, "verbose": "quiet", - "scalar-coloring": true, "tone-mapping": true, "translucency-support": true } diff --git a/testing/baselines/TestConfigFileMultiFileSTL.png b/testing/baselines/TestConfigFileMultiFileSTL.png new file mode 100644 index 0000000000..774f74dec7 --- /dev/null +++ b/testing/baselines/TestConfigFileMultiFileSTL.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ecf2e33e585024ec0ea5b03783d0a2bc4b4aa7f8b24bb2a17129fc60d20c437 +size 33521 diff --git a/testing/baselines/TestConfigFileMultiFileVTP.png b/testing/baselines/TestConfigFileMultiFileVTP.png new file mode 100644 index 0000000000..a98f73117c --- /dev/null +++ b/testing/baselines/TestConfigFileMultiFileVTP.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b6e661b980b215da9abd4b6c6ec516469bd6fb88e78b021247acbbb448f5a46 +size 47342 diff --git a/testing/baselines/TestGLTFDracoImporterWithoutCompression.png b/testing/baselines/TestGLTFDracoImporterWithoutCompression.png new file mode 100644 index 0000000000..2b15acef59 --- /dev/null +++ b/testing/baselines/TestGLTFDracoImporterWithoutCompression.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e3273b3cb9f35e786b59beaa7adc58ea00b456ba7fb9d0d080b9f2fdc681420 +size 1184 diff --git a/testing/baselines/TestGLTFDracoReader.png b/testing/baselines/TestGLTFDracoReader.png deleted file mode 100644 index 2baef14d72..0000000000 --- a/testing/baselines/TestGLTFDracoReader.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d078ab66dbe6d8a5516274e9f7dc196fc6d76897a22e0b8b74a96974646c8f8 -size 2948 diff --git a/testing/baselines/TestGLTFDracoReaderWithoutCompression.png b/testing/baselines/TestGLTFDracoReaderWithoutCompression.png deleted file mode 100644 index 2c2db52fe9..0000000000 --- a/testing/baselines/TestGLTFDracoReaderWithoutCompression.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d1a0da6ad25f8e0843845d8356ce54e0476bd293d4d26768f008e5293d7a1a74 -size 2375 diff --git a/testing/baselines/TestGLTFReaderWithAnimation.png b/testing/baselines/TestGLTFReaderWithAnimation.png deleted file mode 100644 index 2c2db52fe9..0000000000 --- a/testing/baselines/TestGLTFReaderWithAnimation.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d1a0da6ad25f8e0843845d8356ce54e0476bd293d4d26768f008e5293d7a1a74 -size 2375 diff --git a/testing/baselines/TestGroupGeometries.png b/testing/baselines/TestGroupGeometries.png deleted file mode 100644 index 95a8159d03..0000000000 --- a/testing/baselines/TestGroupGeometries.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f04ed30ffa6064f085098e443eaca62b16c8acbb563251bb7413efa53334761f -size 14332 diff --git a/testing/baselines/TestGroupGeometriesColoring.png b/testing/baselines/TestGroupGeometriesColoring.png deleted file mode 100644 index 5ada334e3f..0000000000 --- a/testing/baselines/TestGroupGeometriesColoring.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c4454d7f017687e721c2517a006cd0aac6f83c98520d4e4107a0a184a0d5f490 -size 23226 diff --git a/testing/baselines/TestInteractionActors.png b/testing/baselines/TestInteractionActors.png index f834a5ae6d..7219bdf978 100644 --- a/testing/baselines/TestInteractionActors.png +++ b/testing/baselines/TestInteractionActors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27a6187d63e95f62ed32e7dd83905d1a48f122dac81891ebdac7c0eaa5397c21 -size 59014 +oid sha256:39e23188ffbfa201ec0f1f77c16933f3bdee3e803e4067b8d4b197cda2f3362f +size 59695 diff --git a/testing/baselines/TestInteractionCycleCellInvalidIndex.png b/testing/baselines/TestInteractionCycleCellInvalidIndex.png new file mode 100644 index 0000000000..697d40fe85 --- /dev/null +++ b/testing/baselines/TestInteractionCycleCellInvalidIndex.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0ecb619cc937590f69d684618c0f9409dbeaad586a06a884af637a60ecf6f77 +size 20779 diff --git a/testing/baselines/TestInteractionDirectoryLoop.png b/testing/baselines/TestInteractionDirectoryLoop.png index 1c41df2dec..0eb8577265 100644 --- a/testing/baselines/TestInteractionDirectoryLoop.png +++ b/testing/baselines/TestInteractionDirectoryLoop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:570b3abd89de71240e355bada85d288a63e91feea0f2eeffba07793485d882a6 -size 21106 +oid sha256:38f9875782356c86564fabe11b55eb2480dcbe3ac3013cbc8850015e1fc323fa +size 6648 diff --git a/testing/baselines/TestInteractionGroupGeometriesColoring.png b/testing/baselines/TestInteractionGroupGeometriesColoring.png deleted file mode 100644 index 795a41caf1..0000000000 --- a/testing/baselines/TestInteractionGroupGeometriesColoring.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9598b4c1bc2a8b484a167cff25f2530a14dce63e26887c93e590fbcc836e9ea0 -size 21303 diff --git a/testing/baselines/TestInteractionGroupGeometriesLoadParentDirectory.png b/testing/baselines/TestInteractionGroupGeometriesLoadParentDirectory.png deleted file mode 100644 index b3bd9c3af8..0000000000 --- a/testing/baselines/TestInteractionGroupGeometriesLoadParentDirectory.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d8761068ab79ab4eae6e6acc46b401fb469b5735cc22bac2de3e5810f29b06fb -size 19725 diff --git a/testing/baselines/TestInteractionMultiFileColoring.png b/testing/baselines/TestInteractionMultiFileColoring.png new file mode 100644 index 0000000000..ff4b16377a --- /dev/null +++ b/testing/baselines/TestInteractionMultiFileColoring.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5633d42bb617b0cdb9264bf68d33775f7fe41f011f62606791d55c65680944d9 +size 4754 diff --git a/testing/baselines/TestInteractionGroupGeometriesDrop.png b/testing/baselines/TestInteractionMultiFileDrop.png similarity index 100% rename from testing/baselines/TestInteractionGroupGeometriesDrop.png rename to testing/baselines/TestInteractionMultiFileDrop.png diff --git a/testing/baselines/TestInteractionMultiFileLoadParentDirectory.png b/testing/baselines/TestInteractionMultiFileLoadParentDirectory.png new file mode 100644 index 0000000000..7b074dcd47 --- /dev/null +++ b/testing/baselines/TestInteractionMultiFileLoadParentDirectory.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:426c0a2ff3bd05cdbaf5184d4654e749cdae60f54c9994fdb68d8c45e91f9cb9 +size 7748 diff --git a/testing/baselines/TestInteractionMultiFileVolume.png b/testing/baselines/TestInteractionMultiFileVolume.png new file mode 100644 index 0000000000..8518352d88 --- /dev/null +++ b/testing/baselines/TestInteractionMultiFileVolume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2edf2ecb8bb456ff25a403d7a8274c305d5249f842dab0463c9410fa5a710869 +size 10730 diff --git a/testing/baselines/TestInteractionNoFileCheatsheet.png b/testing/baselines/TestInteractionNoFileCheatsheet.png index b14103d91b..890d453335 100644 --- a/testing/baselines/TestInteractionNoFileCheatsheet.png +++ b/testing/baselines/TestInteractionNoFileCheatsheet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97ab0219e5782422e52c6dfb7f84bfb615a0e48b5f25eb81e8cacb0ce0963aa3 -size 31206 +oid sha256:349a756487644267b5fa4f864b034591b38323608e79df1cb2499d3f14397c4b +size 33401 diff --git a/testing/baselines/TestInteractionNoFileCheatsheetRaytracing.png b/testing/baselines/TestInteractionNoFileCheatsheetRaytracing.png index 34e0d5406e..7ec5b7bfb9 100644 --- a/testing/baselines/TestInteractionNoFileCheatsheetRaytracing.png +++ b/testing/baselines/TestInteractionNoFileCheatsheetRaytracing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc6f7d7759ac9a40be7df35ee0143d4479dea4f479fbe42fc9b63bea88bb9727 -size 31307 +oid sha256:04c5c14bd0bf0182087e147f5b429148a885f1b40abb3d25ef4d0dcbba4fee0b +size 32823 diff --git a/testing/baselines/TestInvalidFileFileName.png b/testing/baselines/TestInvalidFileFileName.png new file mode 100644 index 0000000000..d0e291cfb5 --- /dev/null +++ b/testing/baselines/TestInvalidFileFileName.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d52bf15339fe32774f464e607cd33d036686f295553936599e010d7f0b8665b7 +size 10477 diff --git a/testing/baselines/TestMaterialFullScene.png b/testing/baselines/TestMaterialFullScene.png new file mode 100644 index 0000000000..a4df8df4e1 --- /dev/null +++ b/testing/baselines/TestMaterialFullScene.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00b992a31481c9838b545b3c469631c21c366d9b0826fde6171b56639859f032 +size 15591 diff --git a/testing/baselines/TestMaxSizeAboveMultiFile.png b/testing/baselines/TestMaxSizeAboveMultiFile.png new file mode 100644 index 0000000000..9f5dec9678 --- /dev/null +++ b/testing/baselines/TestMaxSizeAboveMultiFile.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:103fd900202833d926a3662214a6abe92cb3d29af9f2f52d0b8bdb0c529b07ba +size 21417 diff --git a/testing/baselines/TestMetaData.png b/testing/baselines/TestMetaData.png index 7d00fbaea0..43e17e1679 100644 --- a/testing/baselines/TestMetaData.png +++ b/testing/baselines/TestMetaData.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3883d626fc703c5164c4acf9926fe301767976c70771122dc71c705353188b07 -size 6444 +oid sha256:899de118c9ea9a5a748503603c644e40815cd1b9d075076a3ae1d817b4d1fe32 +size 7282 diff --git a/testing/baselines/TestMetaDataImporter.png b/testing/baselines/TestMetaDataImporter.png index dd4e1b7d49..fbd8dae105 100644 --- a/testing/baselines/TestMetaDataImporter.png +++ b/testing/baselines/TestMetaDataImporter.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fa5a91318ed6975f3a7fe7033eba264d166920e81c3c62a0664f28a686b1fd9 -size 2741 +oid sha256:afbdc55430e027dd101652a936f02b30224f0d1e2a519dfa5922e81f3e04c268 +size 5357 diff --git a/testing/baselines/TestMultiFile.png b/testing/baselines/TestMultiFile.png new file mode 100644 index 0000000000..4abd1ca44c --- /dev/null +++ b/testing/baselines/TestMultiFile.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34534186de93d3dc1f9cff24292a4df5dbfabe8486b93f888af6dc47b72f78b1 +size 3335 diff --git a/testing/baselines/TestMultiFileAnimationIndex.png b/testing/baselines/TestMultiFileAnimationIndex.png new file mode 100644 index 0000000000..f8822c3462 --- /dev/null +++ b/testing/baselines/TestMultiFileAnimationIndex.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e788c601396836ab75734bb69ac22ba9605374c12fb635885768fc4e5ef51ebb +size 4947 diff --git a/testing/baselines/TestMultiFileCameraIndex.png b/testing/baselines/TestMultiFileCameraIndex.png new file mode 100644 index 0000000000..d21ae2aafd --- /dev/null +++ b/testing/baselines/TestMultiFileCameraIndex.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d7604226baaaf9eede4666f6cb91e9345b40c15546ea848af933e24ecc185ee +size 3286 diff --git a/testing/baselines/TestMultiFileColoring.png b/testing/baselines/TestMultiFileColoring.png new file mode 100644 index 0000000000..6aaf56bf45 --- /dev/null +++ b/testing/baselines/TestMultiFileColoring.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6ae4ef38117103959dc33fee27c32a041f321568d34e35d9893d47c8bfb1304 +size 5203 diff --git a/testing/baselines/TestMultiFileColoringTexture.png b/testing/baselines/TestMultiFileColoringTexture.png new file mode 100644 index 0000000000..7a427c02a5 --- /dev/null +++ b/testing/baselines/TestMultiFileColoringTexture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae5299f32b0fb18e6d075dd1a0d2da4d0f56e706d68e99c016ff67280fa9cc27 +size 32236 diff --git a/testing/baselines/TestMultiFileInvalidFilesFileName.png b/testing/baselines/TestMultiFileInvalidFilesFileName.png new file mode 100644 index 0000000000..96e26d5183 --- /dev/null +++ b/testing/baselines/TestMultiFileInvalidFilesFileName.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78cfda815b050f116044e73a6582c9574787f642f0d2e938d6fb3dae8920b37d +size 10396 diff --git a/testing/baselines/TestMultiFileMetaData.png b/testing/baselines/TestMultiFileMetaData.png new file mode 100644 index 0000000000..de1422e587 --- /dev/null +++ b/testing/baselines/TestMultiFileMetaData.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b9ba8716f0d3575e34be5bad284b4458075fc8511bfa0f0d9ff32db64dc3b68 +size 9365 diff --git a/testing/baselines/TestMultiFilePositionals.png b/testing/baselines/TestMultiFilePositionals.png new file mode 100644 index 0000000000..005a648478 --- /dev/null +++ b/testing/baselines/TestMultiFilePositionals.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6490419711a39f15432152c92805d9a62316760d3d9dde7f39bba3b03b3387b +size 22177 diff --git a/testing/baselines/TestMultiFileVolume.png b/testing/baselines/TestMultiFileVolume.png new file mode 100644 index 0000000000..babdbc51ef --- /dev/null +++ b/testing/baselines/TestMultiFileVolume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec6f83fda925dd5987e05008df07517bff8d33c52c13c1a3030f545f244bb3be +size 22628 diff --git a/testing/baselines/TestMultiInputArg.png b/testing/baselines/TestMultiInputArg.png index ee490b03f5..005a648478 100644 --- a/testing/baselines/TestMultiInputArg.png +++ b/testing/baselines/TestMultiInputArg.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed967b1fa921b99bb161d2a0a4fc233fcf446f9b41b4a67e6b911f884125fd03 -size 19417 +oid sha256:e6490419711a39f15432152c92805d9a62316760d3d9dde7f39bba3b03b3387b +size 22177 diff --git a/testing/baselines/TestMultiInputMultiArgs.png b/testing/baselines/TestMultiInputMultiArgs.png index ee490b03f5..005a648478 100644 --- a/testing/baselines/TestMultiInputMultiArgs.png +++ b/testing/baselines/TestMultiInputMultiArgs.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed967b1fa921b99bb161d2a0a4fc233fcf446f9b41b4a67e6b911f884125fd03 -size 19417 +oid sha256:e6490419711a39f15432152c92805d9a62316760d3d9dde7f39bba3b03b3387b +size 22177 diff --git a/testing/baselines/TestMultiInputPositionals.png b/testing/baselines/TestMultiInputPositionals.png deleted file mode 100644 index ee490b03f5..0000000000 --- a/testing/baselines/TestMultiInputPositionals.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ed967b1fa921b99bb161d2a0a4fc233fcf446f9b41b4a67e6b911f884125fd03 -size 19417 diff --git a/testing/baselines/TestMultiblockMetaData.png b/testing/baselines/TestMultiblockMetaData.png index 890436e284..dd07c67d85 100644 --- a/testing/baselines/TestMultiblockMetaData.png +++ b/testing/baselines/TestMultiblockMetaData.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c1c24d6933d5e6a5e9ee99917c498959483c4ab432cc5b2d23df15013737572 -size 12772 +oid sha256:e5405e2a1b3dc4d8611c6daba93cafc4618345c4e53984b91fedddcedc5ba9f7 +size 14072 diff --git a/testing/baselines/TestOBJ.png b/testing/baselines/TestOBJ.png index 2d2f517ce7..73957078b6 100644 --- a/testing/baselines/TestOBJ.png +++ b/testing/baselines/TestOBJ.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:904ab06cd17ad6d94322f654b748c67c215c5acfa96617abe6931f8999290bc3 -size 28210 +oid sha256:09ffdd064a6410bdd7a950cd0d0a956163fa270c8180b8b965a4b85ce3232e9e +size 62120 diff --git a/testing/baselines/TestOBJImporter.png b/testing/baselines/TestOBJImporter.png deleted file mode 100644 index 15902b6e88..0000000000 --- a/testing/baselines/TestOBJImporter.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ec623038fe6d6fa1147eced9127d544bf0ac0fafc30c32585d10a5bb50a9d4d5 -size 62120 diff --git a/testing/baselines/TestPythonLoader.png b/testing/baselines/TestPythonLoader.png new file mode 100644 index 0000000000..a288159800 --- /dev/null +++ b/testing/baselines/TestPythonLoader.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2ba67a525ce2c4ed14afcb5dd09c88eec4fef6e5791dcb1a1547bf0e19b32e4 +size 4262 diff --git a/testing/baselines/TestSDKLoader.png b/testing/baselines/TestSDKLoader.png new file mode 100644 index 0000000000..a5a8ecd0b3 --- /dev/null +++ b/testing/baselines/TestSDKLoader.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5203ccd71c4a97024f68063431ed1b570dfa4fba3b1d375abbf48addf4c84c62 +size 4104 diff --git a/testing/baselines/TestTextureColor.png b/testing/baselines/TestTextureColor.png index dacd0edc6c..1fd3daa810 100644 --- a/testing/baselines/TestTextureColor.png +++ b/testing/baselines/TestTextureColor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e563e6a98ca1f4b9c0d7ef1f4074cbcd1165a6b9eb1255a6721f67acca8dcce -size 12736 +oid sha256:d5fec1e684be6ba383e30c1d822c65c68be62f676c406404528e9bc2c7549023 +size 25382 diff --git a/testing/baselines/TestTextureColorWithOptions.png b/testing/baselines/TestTextureColorWithOptions.png index f554a2a245..59d682ce36 100644 --- a/testing/baselines/TestTextureColorWithOptions.png +++ b/testing/baselines/TestTextureColorWithOptions.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58dbaf2048f57d2aa8e895286261da8f85ed16d4b49c172e9a94e9179f55e8e7 -size 13492 +oid sha256:2d4daebd837f6eb890867fc9fc7357679f1a1eceb238ba767eb79200dbf4a185 +size 16018 diff --git a/testing/baselines/TestTextureEmissive.png b/testing/baselines/TestTextureEmissive.png index 1f29a1ede5..ba2b2ca443 100644 --- a/testing/baselines/TestTextureEmissive.png +++ b/testing/baselines/TestTextureEmissive.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be0eaad6b2290a570321bf45cafc8178f65626dd120b449d0e5bd1339ad899d7 -size 10517 +oid sha256:330b9f1e4f006e436177209b78a932a0d39894d6ec5c1debfbbf1593fa24e55e +size 21923 diff --git a/testing/baselines/TestTextureMaterial.png b/testing/baselines/TestTextureMaterial.png index 80aea6ec01..a7564e7eb8 100644 --- a/testing/baselines/TestTextureMaterial.png +++ b/testing/baselines/TestTextureMaterial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:608dd867ccedd9b4eaed6cd0168dddf3c04980d16871650bd9b21242c81b8160 -size 13506 +oid sha256:0968fc83e3bd4533269e9d3f6588af5e9858f4c625f2ffe93f9282bd2667e4e9 +size 19369 diff --git a/testing/baselines/TestTextureMaterialWithOptions.png b/testing/baselines/TestTextureMaterialWithOptions.png index b405571d37..f03e9bceae 100644 --- a/testing/baselines/TestTextureMaterialWithOptions.png +++ b/testing/baselines/TestTextureMaterialWithOptions.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:254ca2285e7d16fcd36ff3b5e38d4d2488d8dc73c6fb5dc7cccf7fa9427ef729 -size 13861 +oid sha256:258098529ba46b22e6658708f9a478b2fb05c8e8f9f3ec6457fce0f2c70341f6 +size 22529 diff --git a/testing/baselines/TestTextureNormal.png b/testing/baselines/TestTextureNormal.png index 5744c83845..4e71b60551 100644 --- a/testing/baselines/TestTextureNormal.png +++ b/testing/baselines/TestTextureNormal.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3a7672b696c57c7b3c8eb36a1ed8dd1a41e7853fe631e08065974a93157182f -size 18072 +oid sha256:0831b8159e803b250b9ccfb5ba4c02632329f6374edbf56c6d97eb7a4c72de0f +size 23351 diff --git a/testing/baselines/TestTextures.png b/testing/baselines/TestTextures.png index cf73c8c65a..67abad3396 100644 --- a/testing/baselines/TestTextures.png +++ b/testing/baselines/TestTextures.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53a47abd2f4b99ed66d5b9c8e94ef330db3105da2bc0e5bf79560425840d6324 -size 25337 +oid sha256:d198c6fa4592560fe7a6a2b8b5d7674a3994e9d80b2296a931938fcdbe2dba4c +size 25632 diff --git a/testing/configs/complex.json b/testing/configs/complex.json index 7bded1e66c..177d1d77ea 100644 --- a/testing/configs/complex.json +++ b/testing/configs/complex.json @@ -7,6 +7,7 @@ }, ".*(vt.)": { + "up": "+Y", "bar": true, "render.effect.ambient_occlusion": true, "filename": true diff --git a/testing/data/mb/recursive/f3d.glb b/testing/data/mb/recursive/f3d.glb new file mode 100644 index 0000000000..bdce4cdb98 --- /dev/null +++ b/testing/data/mb/recursive/f3d.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f4b8990cb6a45a366a724de79f6c996cc2e92abe6161bd78735780d4a2db5b8 +size 3652 diff --git a/testing/data/mb/recursive/mb_1_0.vtp b/testing/data/mb/recursive/mb_1_0.vtp index 24538ba351..4d94f5ba41 100644 --- a/testing/data/mb/recursive/mb_1_0.vtp +++ b/testing/data/mb/recursive/mb_1_0.vtp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da39f580f748e2fbd03ad270b8e3fee354f7165f3652a002610c80172616e02c -size 7084 +oid sha256:96790d19cab995d59dc96556987c281bbfbda1bd46f451d59c556086cdb90138 +size 6519 diff --git a/testing/data/mb/recursive/mb_2_0.vtp b/testing/data/mb/recursive/mb_2_0.vtp index 499c235399..8baae99d75 100644 --- a/testing/data/mb/recursive/mb_2_0.vtp +++ b/testing/data/mb/recursive/mb_2_0.vtp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a35af390f451b6729124015a8b79e6a56bfc1104c98bc5b8e0e01d8776f9f402 -size 6860 +oid sha256:08c5cfddd3aa673f62bbaa16b2201305d6937c91f4cb01c9a1d33c726e14b8e8 +size 6297 diff --git a/testing/recordings/TestInteractionAnimationInvalid.log b/testing/recordings/TestInteractionAnimationInvalid.log deleted file mode 100644 index 30018c65bb..0000000000 --- a/testing/recordings/TestInteractionAnimationInvalid.log +++ /dev/null @@ -1,14 +0,0 @@ -# StreamVersion 1.2 -ConfigureEvent 2 1409 0 0 0 0 0 -ExposeEvent 0 1409 0 0 0 0 0 -RenderEvent 0 1409 0 0 0 0 0 -KeyPressEvent 1698 319 0 32 1 space 0 -CharEvent 1698 319 0 32 1 space 0 -KeyReleaseEvent 1698 319 0 32 1 space 0 -TimerEvent 1698 319 0 32 1 space 0 -TimerEvent 1698 319 0 32 1 space 0 -TimerEvent 1698 319 0 32 1 space 0 -TimerEvent 1698 319 0 32 1 space 0 -KeyPressEvent 1698 319 0 32 1 space 0 -CharEvent 1698 319 0 32 1 space 0 -KeyReleaseEvent 1698 319 0 32 1 space 0 diff --git a/testing/recordings/TestInteractionCycleCellInvalidIndex.log b/testing/recordings/TestInteractionCycleCellInvalidIndex.log new file mode 100644 index 0000000000..388711686b --- /dev/null +++ b/testing/recordings/TestInteractionCycleCellInvalidIndex.log @@ -0,0 +1,16 @@ +# StreamVersion 1.2 +ConfigureEvent 1249 1374 0 0 0 0 0 +ExposeEvent 0 1406 0 0 0 0 0 +RenderEvent 0 1406 0 0 0 0 0 +TimerEvent 0 1406 0 0 0 0 0 +KeyPressEvent -658 227 0 115 1 s 0 +CharEvent -658 227 0 115 1 s 0 +KeyReleaseEvent -658 227 0 115 1 s 0 +TimerEvent -658 227 0 115 1 s 0 +KeyPressEvent -658 227 0 115 1 s 0 +CharEvent -658 227 0 115 1 s 0 +KeyReleaseEvent -658 227 0 115 1 s 0 +TimerEvent -658 227 0 115 1 s 0 +KeyPressEvent -658 227 0 99 1 c 0 +CharEvent -658 227 0 99 1 c 0 +KeyReleaseEvent -658 227 0 99 1 c 0 diff --git a/testing/recordings/TestInteractionDirectoryLoop.log b/testing/recordings/TestInteractionDirectoryLoop.log index db211d0bd4..881ad4ac41 100644 --- a/testing/recordings/TestInteractionDirectoryLoop.log +++ b/testing/recordings/TestInteractionDirectoryLoop.log @@ -10,3 +10,9 @@ KeyReleaseEvent 354 650 0 0 1 Left KeyPressEvent 354 650 0 0 1 Left CharEvent 354 650 0 0 1 Left KeyReleaseEvent 354 650 0 0 1 Left +KeyPressEvent 354 650 0 0 1 Left +CharEvent 354 650 0 0 1 Left +KeyReleaseEvent 354 650 0 0 1 Left +KeyPressEvent 354 650 0 0 1 Left +CharEvent 354 650 0 0 1 Left +KeyReleaseEvent 354 650 0 0 1 Left diff --git a/testing/recordings/TestInteractionGroupGeometriesColoring.log b/testing/recordings/TestInteractionMultiFileColoring.log similarity index 78% rename from testing/recordings/TestInteractionGroupGeometriesColoring.log rename to testing/recordings/TestInteractionMultiFileColoring.log index 964080dfb4..2392cf2816 100644 --- a/testing/recordings/TestInteractionGroupGeometriesColoring.log +++ b/testing/recordings/TestInteractionMultiFileColoring.log @@ -7,6 +7,9 @@ KeyReleaseEvent 474 416 0 115 1 s 0 KeyPressEvent 474 416 0 115 1 s 0 CharEvent 474 416 0 115 1 s 0 KeyReleaseEvent 474 416 0 115 1 s 0 +KeyPressEvent 474 416 0 115 1 s 0 +CharEvent 474 416 0 115 1 s 0 +KeyReleaseEvent 474 416 0 115 1 s 0 KeyPressEvent 474 416 0 98 1 b 0 CharEvent 474 416 0 98 1 b 0 KeyReleaseEvent 474 416 0 98 1 b 0 diff --git a/testing/recordings/TestInteractionGroupGeometriesDrop.log.in b/testing/recordings/TestInteractionMultiFileDrop.log.in similarity index 100% rename from testing/recordings/TestInteractionGroupGeometriesDrop.log.in rename to testing/recordings/TestInteractionMultiFileDrop.log.in diff --git a/testing/recordings/TestInteractionGroupGeometriesLoadParentDirectory.log b/testing/recordings/TestInteractionMultiFileLoadParentDirectory.log similarity index 100% rename from testing/recordings/TestInteractionGroupGeometriesLoadParentDirectory.log rename to testing/recordings/TestInteractionMultiFileLoadParentDirectory.log diff --git a/testing/recordings/TestInteractionMultiFileVolume.log b/testing/recordings/TestInteractionMultiFileVolume.log new file mode 100644 index 0000000000..62157df0ce --- /dev/null +++ b/testing/recordings/TestInteractionMultiFileVolume.log @@ -0,0 +1,21 @@ +# StreamVersion 1.2 +ConfigureEvent 1249 1374 0 0 0 0 0 +ExposeEvent 0 1406 0 0 0 0 0 +RenderEvent 0 1406 0 0 0 0 0 +TimerEvent 0 1406 0 0 0 0 0 +KeyPressEvent 237 552 0 115 1 s 0 +CharEvent 237 552 0 115 1 s 0 +KeyReleaseEvent 237 552 0 115 1 s 0 +TimerEvent 237 552 0 115 1 s 0 +KeyPressEvent 237 552 0 115 1 s 0 +CharEvent 237 552 0 115 1 s 0 +KeyReleaseEvent 237 552 0 115 1 s 0 +TimerEvent 237 552 0 115 1 s 0 +KeyPressEvent 237 552 0 118 1 v 0 +CharEvent 237 552 0 118 1 v 0 +KeyReleaseEvent 237 552 0 118 1 v 0 +TimerEvent 237 552 0 118 1 v 0 +TimerEvent 237 552 0 118 1 v 0 +KeyPressEvent 237 552 0 98 1 b 0 +CharEvent 237 552 0 98 1 b 0 +KeyReleaseEvent 237 552 0 98 1 b 0 diff --git a/testing/recordings/TestInteractionVolume.log b/testing/recordings/TestInteractionVolume.log deleted file mode 100644 index 7cefc84c39..0000000000 --- a/testing/recordings/TestInteractionVolume.log +++ /dev/null @@ -1,3 +0,0 @@ -# StreamVersion 1.1 -ExposeEvent 0 599 0 0 0 0 -RenderEvent 0 599 0 0 0 0 diff --git a/vtkext/private/module/CMakeLists.txt b/vtkext/private/module/CMakeLists.txt index 357dd6372f..1ec6b34107 100644 --- a/vtkext/private/module/CMakeLists.txt +++ b/vtkext/private/module/CMakeLists.txt @@ -40,6 +40,7 @@ set(classes vtkF3DInteractorEventRecorder vtkF3DInteractorStyle vtkF3DMemoryMesh + vtkF3DMetaImporter vtkF3DNoRenderWindow vtkF3DObjectFactory vtkF3DOpenGLGridMapper diff --git a/vtkext/private/module/Testing/CMakeLists.txt b/vtkext/private/module/Testing/CMakeLists.txt index bd1b4fcbaa..93f6579b7c 100644 --- a/vtkext/private/module/Testing/CMakeLists.txt +++ b/vtkext/private/module/Testing/CMakeLists.txt @@ -1,11 +1,11 @@ set(test_sources TestF3DCachedTexturesPrint.cxx TestF3DGenericImporter.cxx - TestF3DGenericImporterMultiColoring.cxx TestF3DInteractorEventRecorder.cxx TestF3DLog.cxx + TestF3DMetaImporterMultiColoring.cxx TestF3DObjectFactory.cxx - TestF3DOpenGLGridMapper.cxx + TestF3DOpenGLGridMapper.cxx TestF3DRenderPass.cxx TestF3DRendererWithColoring.cxx ) @@ -15,7 +15,7 @@ if(WIN32 AND F3D_WINDOWS_GUI) endif() if(F3D_MODULE_EXR) - list(APPEND test_sources + list(APPEND test_sources TestF3DEXRReader.cxx TestF3DEXRReaderInvalid.cxx) endif() @@ -32,8 +32,12 @@ if(UNIX) set_tests_properties(f3d::vtkextPrivateCxx-TestF3DLog PROPERTIES PASS_REGULAR_EXPRESSION "Test Info Test Warning Test Error\nTest Debug Test Info Test Warning Test Error\nTest Warning Test Error\nTest Error\nTest Info Coloring Test Warning Coloring Test Error Coloring\n") endif() +set_tests_properties(f3d::vtkextPrivateCxx-TestF3DGenericImporter + PROPERTIES + FAIL_REGULAR_EXPRESSION "") + if(F3D_MODULE_EXR) set_tests_properties(f3d::vtkextPrivateCxx-TestF3DEXRReaderInvalid - PROPERTIES + PROPERTIES FAIL_REGULAR_EXPRESSION "") endif() diff --git a/vtkext/private/module/Testing/TestF3DGenericImporter.cxx b/vtkext/private/module/Testing/TestF3DGenericImporter.cxx index fbe9ffacd8..5aedef939c 100644 --- a/vtkext/private/module/Testing/TestF3DGenericImporter.cxx +++ b/vtkext/private/module/Testing/TestF3DGenericImporter.cxx @@ -3,6 +3,7 @@ #include #include #include +#include #include "vtkF3DGenericImporter.h" @@ -11,15 +12,6 @@ int TestF3DGenericImporter(int argc, char* argv[]) { vtkNew importer; - - // Test without any reader - if (importer->CanReadFile()) - { - std::cerr << "Importer unexpectedly can read a file without internal reader" << std::endl; - return EXIT_FAILURE; - } - importer->Update(); - importer->Print(cout); if (importer->GetNumberOfAnimations() != 0) { std::cerr << "Unexpected number of animations" << std::endl; @@ -44,13 +36,7 @@ int TestF3DGenericImporter(int argc, char* argv[]) reader->UpdateInformation(); reader->EnableAnimation(0); - importer->AddInternalReader("Test", reader); - if (!importer->CanReadFile()) - { - std::cerr << "Importer unexpectedly can not read a valid file" << std::endl; - return EXIT_FAILURE; - } - + importer->SetInternalReader(reader); importer->Update(); importer->Print(cout); if (importer->GetNumberOfAnimations() != 1) @@ -73,24 +59,20 @@ int TestF3DGenericImporter(int argc, char* argv[]) return EXIT_FAILURE; } - // Test with multiple readers - vtkNew reader2; - filename = std::string(argv[1]) + "data/suzanne.ply"; - reader2->SetFileName(filename.c_str()); - importer->AddInternalReader("Test2", reader2); +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240910) + filename = std::string(argv[1]) + "data/BoxAnimated_invalid_animation.gltf"; + reader->SetFileName(filename.c_str()); + reader->UpdateInformation(); + reader->EnableAnimation(0); - if (!importer->CanReadFile()) - { - std::cerr << "Importer unexpectedly can not read a valid file" << std::endl; - return EXIT_FAILURE; - } + importer->SetInternalReader(reader); importer->Update(); - std::string description = importer->GetMetaDataDescription(); - if (description.find("Number of geometries: 2") == std::string::npos) + if (importer->UpdateAtTimeValue(0.1)) { - std::cerr << "Unexpected meta data description with multiple geometries" << std::endl; + std::cerr << "Unexpected UpdateAtTimeValue success" << std::endl; return EXIT_FAILURE; } +#endif // Static method testing if (vtkF3DGenericImporter::GetDataObjectDescription(nullptr) != "") diff --git a/vtkext/private/module/Testing/TestF3DGenericImporterMultiColoring.cxx b/vtkext/private/module/Testing/TestF3DMetaImporterMultiColoring.cxx similarity index 58% rename from vtkext/private/module/Testing/TestF3DGenericImporterMultiColoring.cxx rename to vtkext/private/module/Testing/TestF3DMetaImporterMultiColoring.cxx index 8b7cddcecb..97f220253f 100644 --- a/vtkext/private/module/Testing/TestF3DGenericImporterMultiColoring.cxx +++ b/vtkext/private/module/Testing/TestF3DMetaImporterMultiColoring.cxx @@ -1,35 +1,74 @@ #include #include +#include +#include #include #include #include #include "vtkF3DGenericImporter.h" +#include "vtkF3DMetaImporter.h" #include -int TestF3DGenericImporterMultiColoring(int argc, char* argv[]) +int TestF3DMetaImporterMultiColoring(int argc, char* argv[]) { - vtkNew importer; + vtkNew importer; + + // Check importer error code path + if (!importer->GetAnimationName(0).empty()) + { + std::cerr << "Unexpected animation name that should be empty" << std::endl; + return EXIT_FAILURE; + } + if (importer->IsAnimationEnabled(0)) + { + std::cerr << "Unexpected enabled animation that should not be" << std::endl; + return EXIT_FAILURE; + } + + int nbTimeSteps; + double timeRange[2]; + if (importer->GetTemporalInformation(0, 60, nbTimeSteps, timeRange, nullptr)) + { + std::cerr << "Unexpected enabled animation that should not be" << std::endl; + return EXIT_FAILURE; + } + + if (!importer->GetCameraName(0).empty()) + { + std::cerr << "Unexpected camera name that should be empty" << std::endl; + return EXIT_FAILURE; + } + + if (importer->FindIndexForColoring(false, "") != -1) + { + std::cerr << "Unexpected FindIndexForColoring success" << std::endl; + return EXIT_FAILURE; + } + // Read a vts and a vtu with same array names // but different component names and array ranges vtkNew readerVTU; std::string filename = std::string(argv[1]) + "data/bluntfin_t.vtu"; readerVTU->SetFileName(filename.c_str()); - importer->AddInternalReader("VTU", readerVTU); + vtkNew importerVTU; + importerVTU->SetInternalReader(readerVTU); vtkNew readerVTS; filename = std::string(argv[1]) + "data/bluntfin.vts"; readerVTS->SetFileName(filename.c_str()); - importer->AddInternalReader("VTS", readerVTS); + vtkNew importerVTS; + importerVTS->SetInternalReader(readerVTS); - if (!importer->CanReadFile()) - { - std::cerr << "Importer unexpectedly can not read valid files" << std::endl; - return EXIT_FAILURE; - } + importer->AddImporter(importerVTU); + importer->AddImporter(importerVTS); + vtkNew window; + vtkNew renderer; + window->AddRenderer(renderer); + importer->SetRenderWindow(window); importer->Update(); if (importer->GetNumberOfIndexesForColoring(false) != 3) @@ -45,16 +84,11 @@ int TestF3DGenericImporterMultiColoring(int argc, char* argv[]) return EXIT_FAILURE; } - vtkF3DGenericImporter::ColoringInfo info; + vtkF3DMetaImporter::ColoringInfo info; importer->GetInfoForColoring(false, idx, info); if (info.Name != "Momentum") { - std::cerr << "Unexpected coloring name" << std::endl; - return EXIT_FAILURE; - } - if (info.Arrays.size() != 2) - { - std::cerr << "Unexpected number of arrays" << std::endl; + std::cerr << "Unexpected coloring name: " << info.Name << std::endl; return EXIT_FAILURE; } if (info.MaximumNumberOfComponents != 3) @@ -78,13 +112,13 @@ int TestF3DGenericImporterMultiColoring(int argc, char* argv[]) return EXIT_FAILURE; } if (!vtkMathUtilities::FuzzyCompare(info.ComponentRanges[0][0], -5.49586, 1e-5) || - !vtkMathUtilities::FuzzyCompare(info.ComponentRanges[0][1], 5.0455, 1e-5)) + !vtkMathUtilities::FuzzyCompare(info.ComponentRanges[0][1], 5.79029, 1e-5)) { std::cerr << "Unexpected coloring component range" << std::endl; return EXIT_FAILURE; } if (!vtkMathUtilities::FuzzyCompare(info.MagnitudeRange[0], 0., 1e-5) || - !vtkMathUtilities::FuzzyCompare(info.MagnitudeRange[1], 5.71596, 1e-5)) + !vtkMathUtilities::FuzzyCompare(info.MagnitudeRange[1], 6.25568, 1e-5)) { std::cerr << "Unexpected coloring magnitude range" << std::endl; return EXIT_FAILURE; diff --git a/vtkext/private/module/Testing/TestF3DRendererWithColoring.cxx b/vtkext/private/module/Testing/TestF3DRendererWithColoring.cxx index dc1db79335..9a4deafc6d 100644 --- a/vtkext/private/module/Testing/TestF3DRendererWithColoring.cxx +++ b/vtkext/private/module/Testing/TestF3DRendererWithColoring.cxx @@ -1,5 +1,6 @@ #include #include +#include #include #include "vtkF3DConfigure.h" @@ -9,31 +10,24 @@ int TestF3DRendererWithColoring(int argc, char* argv[]) { vtkNew renderer; + vtkNew importer; + vtkNew window; - // Check some invalid code path - renderer->ShowGrid(true); - - // Check error paths - if (renderer->GetColoringArrayName().has_value()) - { - std::cerr << "Unexpected coloring information without an importer" << std::endl; - return EXIT_FAILURE; - } - if (!renderer->GetColoringDescription().empty()) - { - std::cerr << "Unexpected coloring description without an importer" << std::endl; - return EXIT_FAILURE; - } - - vtkNew importer; + window->AddRenderer(renderer); + importer->SetRenderWindow(window); renderer->SetImporter(importer); - // Read a vts and a vtu with same array names - // but different component names and array ranges + // Check invalid bounding box code path + renderer->ShowGrid(true); + renderer->UpdateActors(); + vtkNew readerVTU; std::string filename = std::string(argv[1]) + "data/bluntfin_t.vtu"; readerVTU->SetFileName(filename.c_str()); - importer->AddInternalReader("VTU", readerVTU); + vtkNew importerVTU; + importerVTU->SetInternalReader(readerVTU); + + importer->AddImporter(importerVTU); importer->Update(); // Check invalid array code path diff --git a/vtkext/private/module/vtkF3DGenericImporter.cxx b/vtkext/private/module/vtkF3DGenericImporter.cxx index 49c07d72b7..d888d35ca3 100644 --- a/vtkext/private/module/vtkF3DGenericImporter.cxx +++ b/vtkext/private/module/vtkF3DGenericImporter.cxx @@ -1,65 +1,37 @@ #include "vtkF3DGenericImporter.h" +#include "vtkF3DPostProcessFilter.h" #include "F3DLog.h" #include -#include -#include -#include -#include -#include +#include #include #include #include #include #include #include -#include -#include #include #include #include #include -#include -#include +#include #include -#include +#include +#include #include -struct ReaderPipeline +struct vtkF3DGenericImporter::Internals { - ReaderPipeline() - { - this->GeometryActor->GetProperty()->SetInterpolationToPBR(); - this->PolyDataMapper->InterpolateScalarsBeforeMappingOn(); - } - - std::string Name; - bool Imported = false; - vtkSmartPointer Reader; + vtkSmartPointer Reader = nullptr; vtkNew PostPro; - std::string OutputDescription; - vtkNew GeometryActor; - vtkNew PointSpritesActor; - vtkNew VolumeProp; vtkNew PolyDataMapper; - vtkNew PointGaussianMapper; - vtkSmartPointer VolumeMapper; - - vtkDataSet* Output = nullptr; - vtkDataSetAttributes* PointDataForColoring = nullptr; - vtkDataSetAttributes* CellDataForColoring = nullptr; -}; - -struct vtkF3DGenericImporter::Internals -{ - std::vector Readers; + std::string OutputDescription; - std::vector PointDataArrayVectorForColoring; - std::vector CellDataArrayVectorForColoring; - vtkBoundingBox GeometryBoundingBox; + vtkPolyData* ImportedPoints = nullptr; + vtkImageData* ImportedImage = nullptr; bool HasAnimation = false; bool AnimationEnabled = false; @@ -77,23 +49,16 @@ vtkF3DGenericImporter::vtkF3DGenericImporter() //---------------------------------------------------------------------------- void vtkF3DGenericImporter::UpdateTemporalInformation() { + assert(this->Pimpl->Reader); this->Pimpl->HasAnimation = false; - this->Pimpl->TimeRange[0] = std::numeric_limits::infinity(); - this->Pimpl->TimeRange[1] = -std::numeric_limits::infinity(); - - // Update each reader - for (ReaderPipeline& pipe : this->Pimpl->Readers) + this->Pimpl->Reader->UpdateInformation(); + vtkInformation* readerInfo = this->Pimpl->Reader->GetOutputInformation(0); + if (readerInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_RANGE())) { - pipe.Reader->UpdateInformation(); - vtkInformation* readerInfo = pipe.Reader->GetOutputInformation(0); - if (readerInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_RANGE())) - { - // Accumulate time ranges - double* readerTimeRange = readerInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_RANGE()); - this->Pimpl->TimeRange[0] = std::min(this->Pimpl->TimeRange[0], readerTimeRange[0]); - this->Pimpl->TimeRange[1] = std::max(this->Pimpl->TimeRange[1], readerTimeRange[1]); - this->Pimpl->HasAnimation = true; - } + double* readerTimeRange = readerInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_RANGE()); + this->Pimpl->TimeRange[0] = readerTimeRange[0]; + this->Pimpl->TimeRange[1] = readerTimeRange[1]; + this->Pimpl->HasAnimation = true; } } @@ -104,42 +69,47 @@ vtkIdType vtkF3DGenericImporter::GetNumberOfAnimations() } //---------------------------------------------------------------------------- -std::string vtkF3DGenericImporter::GetAnimationName(vtkIdType animationIndex) +std::string vtkF3DGenericImporter::GetAnimationName([[maybe_unused]] vtkIdType animationIndex) { - return animationIndex < this->GetNumberOfAnimations() ? "default" : ""; + assert(animationIndex == 0); + return this->Pimpl->HasAnimation ? "default" : ""; } //---------------------------------------------------------------------------- -void vtkF3DGenericImporter::EnableAnimation(vtkIdType animationIndex) +void vtkF3DGenericImporter::EnableAnimation([[maybe_unused]] vtkIdType animationIndex) { - if (animationIndex < this->GetNumberOfAnimations()) + assert(animationIndex == 0); + if (this->Pimpl->HasAnimation) { this->Pimpl->AnimationEnabled = true; } } //---------------------------------------------------------------------------- -void vtkF3DGenericImporter::DisableAnimation(vtkIdType animationIndex) +void vtkF3DGenericImporter::DisableAnimation([[maybe_unused]] vtkIdType animationIndex) { - if (animationIndex < this->GetNumberOfAnimations()) + assert(animationIndex == 0); + if (this->Pimpl->HasAnimation) { this->Pimpl->AnimationEnabled = false; } } //---------------------------------------------------------------------------- -bool vtkF3DGenericImporter::IsAnimationEnabled(vtkIdType animationIndex) +bool vtkF3DGenericImporter::IsAnimationEnabled([[maybe_unused]] vtkIdType animationIndex) { - return animationIndex < this->GetNumberOfAnimations() ? this->Pimpl->AnimationEnabled : false; + assert(animationIndex == 0); + return this->Pimpl->AnimationEnabled; } //---------------------------------------------------------------------------- -bool vtkF3DGenericImporter::GetTemporalInformation(vtkIdType animationIndex, +bool vtkF3DGenericImporter::GetTemporalInformation([[maybe_unused]] vtkIdType animationIndex, double vtkNotUsed(frameRate), int& vtkNotUsed(nbTimeSteps), double timeRange[2], vtkDoubleArray* vtkNotUsed(timeSteps)) { + assert(animationIndex == 0); // F3D do not care about timesteps - if (animationIndex < this->GetNumberOfAnimations()) + if (this->Pimpl->HasAnimation) { timeRange[0] = this->Pimpl->TimeRange[0]; timeRange[1] = this->Pimpl->TimeRange[1]; @@ -151,153 +121,68 @@ bool vtkF3DGenericImporter::GetTemporalInformation(vtkIdType animationIndex, //---------------------------------------------------------------------------- void vtkF3DGenericImporter::ImportActors(vtkRenderer* ren) { - this->Pimpl->GeometryBoundingBox.Reset(); - bool hasGeometry = false; - - // Update each reader - for (size_t readerIndex = 0; readerIndex < this->Pimpl->Readers.size(); readerIndex++) + assert(this->Pimpl->Reader); + + // Read file and forward progress + vtkNew progressForwarder; + progressForwarder->SetTarget(this); + this->Pimpl->Reader->AddObserver(vtkCommand::ProgressEvent, progressForwarder); + bool status = this->Pimpl->PostPro->GetExecutive()->Update(); + if (!status || !this->Pimpl->Reader->GetOutputDataObject(0)) { - ReaderPipeline& pipe = this->Pimpl->Readers[readerIndex]; - if (pipe.Imported) - { - continue; - } - - { - // Forward progress event - vtkNew progressCallback; - double progressRatio = static_cast(readerIndex + 1) / this->Pimpl->Readers.size(); - std::pair progressPair = { this, progressRatio }; - progressCallback->SetClientData(&progressPair); - progressCallback->SetCallback( - [](vtkObject*, unsigned long, void* clientData, void* callData) - { - auto localPair = static_cast*>(clientData); - vtkF3DGenericImporter* self = localPair->first; - double progress = *static_cast(callData) * localPair->second; - self->InvokeEvent(vtkCommand::ProgressEvent, &progress); - }); - pipe.Reader->AddObserver(vtkCommand::ProgressEvent, progressCallback); - bool status = pipe.PostPro->GetExecutive()->Update(); - pipe.Reader->RemoveObservers(vtkCommand::ProgressEvent); - - if (!status || !pipe.Reader->GetOutputDataObject(0)) - { - F3DLog::Print(F3DLog::Severity::Warning, "A reader failed to update"); - pipe.Output = nullptr; - continue; - } - } - - // Cast to dataset types - vtkPolyData* surface = vtkPolyData::SafeDownCast(pipe.PostPro->GetOutput()); - vtkImageData* image = vtkImageData::SafeDownCast(pipe.PostPro->GetOutput(2)); - vtkDataSet* dataSet = vtkImageData::SafeDownCast(pipe.PostPro->GetInput()) - ? vtkDataSet::SafeDownCast(image) - : vtkDataSet::SafeDownCast(surface); - pipe.Output = dataSet; - - // Recover output description from the reader - pipe.OutputDescription = vtkF3DGenericImporter::GetDataObjectDescription(pipe.Reader->GetOutputDataObject(0)); - - // Recover data for coloring - pipe.PointDataForColoring = vtkDataSetAttributes::SafeDownCast(dataSet->GetPointData()); - pipe.CellDataForColoring = vtkDataSetAttributes::SafeDownCast(dataSet->GetCellData()); - - // Increase bounding box size if needed - double bounds[6]; - surface->GetBounds(bounds); - this->Pimpl->GeometryBoundingBox.AddBounds(bounds); + this->SetFailureStatus(); + return; + } - // Add filter outputs to mapper inputs - pipe.PolyDataMapper->SetInputConnection(pipe.PostPro->GetOutputPort(0)); - pipe.PointGaussianMapper->SetInputConnection(pipe.PostPro->GetOutputPort(1)); + // Cast to dataset types + this->Pimpl->ImportedPoints = vtkPolyData::SafeDownCast(this->Pimpl->PostPro->GetOutput(1)); + vtkImageData* image = vtkImageData::SafeDownCast(this->Pimpl->PostPro->GetOutput(2)); + this->Pimpl->ImportedImage = image->GetNumberOfCells() > 0 ? image : nullptr; - if (!pipe.VolumeMapper) - { - pipe.VolumeMapper = vtkSmartPointer::New(); - pipe.VolumeMapper->SetRequestedRenderModeToGPU(); - } - pipe.VolumeMapper->SetInputConnection(pipe.PostPro->GetOutputPort(2)); + // Recover output description from the reader + this->Pimpl->OutputDescription = vtkF3DGenericImporter::GetDataObjectDescription(this->Pimpl->Reader->GetOutputDataObject(0)); - // Set geometry actor default properties - pipe.GeometryActor->GetProperty()->SetPointSize(10.0); - pipe.GeometryActor->GetProperty()->SetLineWidth(1.0); + // Add filter outputs to mapper inputs + this->Pimpl->PolyDataMapper->SetInputConnection(this->Pimpl->PostPro->GetOutputPort(0)); + this->Pimpl->PolyDataMapper->ScalarVisibilityOff(); - // add mappers - pipe.VolumeProp->SetMapper(pipe.VolumeMapper); - pipe.GeometryActor->SetMapper(pipe.PolyDataMapper); - pipe.PointSpritesActor->SetMapper(pipe.PointGaussianMapper); + // Set geometry actor default properties + // Rely on vtkProperty default for other properties + this->Pimpl->GeometryActor->GetProperty()->SetPointSize(10.0); + this->Pimpl->GeometryActor->GetProperty()->SetLineWidth(1.0); + this->Pimpl->GeometryActor->GetProperty()->SetRoughness(0.3); + this->Pimpl->GeometryActor->GetProperty()->SetInterpolationToPBR(); - // add props - ren->AddActor(pipe.GeometryActor); - ren->AddActor(pipe.PointSpritesActor); - ren->AddVolume(pipe.VolumeProp); + // add mappers + // TODO implement proper composite support + this->Pimpl->GeometryActor->SetMapper(this->Pimpl->PolyDataMapper); - // Set visibilities - pipe.GeometryActor->VisibilityOff(); - pipe.PointSpritesActor->VisibilityOff(); - pipe.VolumeProp->VisibilityOff(); + // add props + ren->AddActor(this->Pimpl->GeometryActor); +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + this->ActorCollection->AddItem(this->Pimpl->GeometryActor); +#endif - pipe.Imported = true; - hasGeometry = true; - } + // Set visibilities + this->Pimpl->GeometryActor->VisibilityOn(); this->UpdateTemporalInformation(); - this->UpdateColoringVectors(false); - this->UpdateColoringVectors(true); - if (!hasGeometry) - { - this->SetFailureStatus(); - } } //---------------------------------------------------------------------------- -void vtkF3DGenericImporter::PrintSelf(std::ostream& os, vtkIndent indent) -{ - vtkImporter::PrintSelf(os, indent); -} - -//---------------------------------------------------------------------------- -void vtkF3DGenericImporter::AddInternalReader(const std::string& name, vtkAlgorithm* reader) +void vtkF3DGenericImporter::SetInternalReader(vtkAlgorithm* reader) { if (reader) { - ReaderPipeline pipe; - pipe.Name = name; - pipe.Reader = reader; - pipe.PostPro->SetInputConnection(pipe.Reader->GetOutputPort()); - this->Pimpl->Readers.push_back(std::move(pipe)); - this->Modified(); + this->Pimpl->Reader = reader; + this->Pimpl->PostPro->SetInputConnection(this->Pimpl->Reader->GetOutputPort()); } } -//---------------------------------------------------------------------------- -void vtkF3DGenericImporter::RemoveInternalReaders() -{ - this->Pimpl->Readers.clear(); - this->Modified(); -} - -//---------------------------------------------------------------------------- -bool vtkF3DGenericImporter::CanReadFile() -{ - return this->Pimpl->Readers.size() > 0 && this->Pimpl->Readers[0].Reader != nullptr; -} - //---------------------------------------------------------------------------- std::string vtkF3DGenericImporter::GetOutputsDescription() { - std::string description; - for (const ReaderPipeline& pipe : this->Pimpl->Readers) - { - if (this->Pimpl->Readers.size() > 0) - { - description += "=== " + pipe.Name + " ===\n"; - } - description += pipe.OutputDescription; - } - return description; + return this->Pimpl->OutputDescription; } //---------------------------------------------------------------------------- @@ -371,257 +256,37 @@ std::string vtkF3DGenericImporter::GetDataObjectDescription(vtkDataObject* objec return ""; } -//---------------------------------------------------------------------------- -std::string vtkF3DGenericImporter::GetMetaDataDescription() -{ - vtkIdType nPoints = 0; - vtkIdType nCells = 0; - for (ReaderPipeline& pipe : this->Pimpl->Readers) - { - vtkDataObject* object = pipe.Reader->GetOutputDataObject(0); - if (object) - { - nPoints += object->GetNumberOfElements(vtkDataObject::POINT); - nCells += object->GetNumberOfElements(vtkDataObject::CELL); - } - } - - std::string description; - if (this->Pimpl->Readers.size() > 1) - { - description += "Number of geometries: "; - description += std::to_string(this->Pimpl->Readers.size()); - description += "\n"; - } - description += "Number of points: "; - description += std::to_string(nPoints); - description += "\n"; - description += "Number of cells: "; - description += std::to_string(nCells); - return description; -} - //---------------------------------------------------------------------------- bool vtkF3DGenericImporter::UpdateAtTimeValue(double timeValue) { - // Update each reader - bool hasGeometry = false; - for (ReaderPipeline& pipe : this->Pimpl->Readers) + assert(this->Pimpl->Reader); + if(!this->Pimpl->PostPro->UpdateTimeStep(timeValue) || !this->Pimpl->Reader->GetOutputDataObject(0)) { - if(!pipe.PostPro->UpdateTimeStep(timeValue) || !pipe.Reader->GetOutputDataObject(0)) - { - F3DLog::Print(F3DLog::Severity::Warning, "A reader failed to update at a timeValue"); - pipe.Output = nullptr; - pipe.Imported = false; - continue; - } - hasGeometry = true; + F3DLog::Print(F3DLog::Severity::Warning, "A reader failed to update at a timeValue"); + return false; } - this->UpdateColoringVectors(false); - this->UpdateColoringVectors(true); this->UpdateOutputDescriptions(); - return hasGeometry; -} - -//---------------------------------------------------------------------------- -std::vector> -vtkF3DGenericImporter::GetGeometryActorsAndMappers() -{ - std::vector> actorsAndMappers( - this->Pimpl->Readers.size()); - - std::transform(this->Pimpl->Readers.cbegin(), this->Pimpl->Readers.cend(), - actorsAndMappers.begin(), - [](const ReaderPipeline& pipe) - { return std::make_pair(pipe.GeometryActor.Get(), pipe.PolyDataMapper.Get()); }); - - return actorsAndMappers; -} - -//---------------------------------------------------------------------------- -std::vector> -vtkF3DGenericImporter::GetPointSpritesActorsAndMappers() -{ - std::vector> actorsAndMappers( - this->Pimpl->Readers.size()); - - std::transform(this->Pimpl->Readers.cbegin(), this->Pimpl->Readers.cend(), - actorsAndMappers.begin(), - [](const ReaderPipeline& pipe) - { return std::make_pair(pipe.PointSpritesActor.Get(), pipe.PointGaussianMapper.Get()); }); - - return actorsAndMappers; -} - -//---------------------------------------------------------------------------- -std::vector> -vtkF3DGenericImporter::GetVolumePropsAndMappers() -{ - std::vector> propsAndMappers( - this->Pimpl->Readers.size()); - - std::transform(this->Pimpl->Readers.cbegin(), this->Pimpl->Readers.cend(), - propsAndMappers.begin(), - [](const ReaderPipeline& pipe) - { return std::make_pair(pipe.VolumeProp.Get(), pipe.VolumeMapper.Get()); }); - - return propsAndMappers; -} - -//---------------------------------------------------------------------------- -void vtkF3DGenericImporter::UpdateColoringVectors(bool useCellData) -{ - // Recover all possible names - std::set arrayNames; - for (ReaderPipeline& pipe : this->Pimpl->Readers) - { - if (!pipe.Output) - { - continue; - } - - vtkDataSetAttributes* attr = useCellData - ? static_cast(pipe.Output->GetCellData()) - : static_cast(pipe.Output->GetPointData()); - - for (int i = 0; i < attr->GetNumberOfArrays(); i++) - { - vtkDataArray* array = attr->GetArray(i); - if (array && array->GetName()) - { - arrayNames.insert(array->GetName()); - } - } - } - - auto& data = useCellData ? this->Pimpl->CellDataArrayVectorForColoring - : this->Pimpl->PointDataArrayVectorForColoring; - data.clear(); - - // Create a vector of arrays by name - for (const std::string& arrayName : arrayNames) - { - vtkF3DGenericImporter::ColoringInfo info; - info.Name = arrayName; - for (ReaderPipeline& pipe : this->Pimpl->Readers) - { - if (!pipe.Output) - { - continue; - } - - vtkDataArray* array = useCellData ? pipe.Output->GetCellData()->GetArray(arrayName.c_str()) - : pipe.Output->GetPointData()->GetArray(arrayName.c_str()); - if (array) - { - info.MaximumNumberOfComponents = - std::max(info.MaximumNumberOfComponents, array->GetNumberOfComponents()); - - // Set ranges - // XXX this does not take animation into account - std::array range; - array->GetRange(range.data(), -1); - info.MagnitudeRange[0] = std::min(info.MagnitudeRange[0], range[0]); - info.MagnitudeRange[1] = std::max(info.MagnitudeRange[1], range[1]); - - for (size_t i = 0; i < static_cast(array->GetNumberOfComponents()); i++) - { - array->GetRange(range.data(), static_cast(i)); - if (i < info.ComponentRanges.size()) - { - info.ComponentRanges[i][0] = std::min(info.ComponentRanges[i][0], range[0]); - info.ComponentRanges[i][1] = std::max(info.ComponentRanges[i][1], range[1]); - } - else - { - info.ComponentRanges.emplace_back(range); - } - } - - // Set component names - if (array->HasAComponentName()) - { - for (size_t i = 0; i < static_cast(array->GetNumberOfComponents()); i++) - { - const char* compName = array->GetComponentName(i); - if (i < info.ComponentNames.size()) - { - if (compName && info.ComponentNames[i] != std::string(compName)) - { - // set non-coherent component names to empty string - info.ComponentNames[i] = ""; - } - } - else - { - // Add components names to the back of the component names vector - info.ComponentNames.emplace_back(compName ? compName : ""); - } - } - } - } - info.Arrays.emplace_back(array); - } - - data.emplace_back(info); - } -} - -//---------------------------------------------------------------------------- -bool vtkF3DGenericImporter::GetInfoForColoring( - bool useCellData, int index, vtkF3DGenericImporter::ColoringInfo& info) -{ - auto& data = useCellData ? this->Pimpl->CellDataArrayVectorForColoring - : this->Pimpl->PointDataArrayVectorForColoring; - - if (index < 0 || index >= static_cast(data.size())) - { - return false; - } - info = data[index]; return true; } //---------------------------------------------------------------------------- -int vtkF3DGenericImporter::GetNumberOfIndexesForColoring(bool useCellData) -{ - auto& data = useCellData ? this->Pimpl->CellDataArrayVectorForColoring - : this->Pimpl->PointDataArrayVectorForColoring; - return static_cast(data.size()); -} - -//---------------------------------------------------------------------------- -int vtkF3DGenericImporter::FindIndexForColoring(bool useCellData, const std::string& arrayName) +void vtkF3DGenericImporter::UpdateOutputDescriptions() { - auto& data = useCellData ? this->Pimpl->CellDataArrayVectorForColoring - : this->Pimpl->PointDataArrayVectorForColoring; - for (size_t i = 0; i < data.size(); i++) - { - if (data[i].Name == arrayName) - { - return static_cast(i); - } - } - return -1; + assert(this->Pimpl->Reader); + // Recover output description + vtkDataObject* readerOutput = this->Pimpl->Reader->GetOutputDataObject(0); + this->Pimpl->OutputDescription = vtkF3DGenericImporter::GetDataObjectDescription(readerOutput); } //---------------------------------------------------------------------------- -const vtkBoundingBox& vtkF3DGenericImporter::GetGeometryBoundingBox() +vtkPolyData* vtkF3DGenericImporter::GetImportedPoints() { - return this->Pimpl->GeometryBoundingBox; + return this->Pimpl->ImportedPoints; } //---------------------------------------------------------------------------- -void vtkF3DGenericImporter::UpdateOutputDescriptions() +vtkImageData* vtkF3DGenericImporter::GetImportedImage() { - for (ReaderPipeline& pipe : this->Pimpl->Readers) - { - if (pipe.Imported) - { - // Recover output description - vtkDataObject* readerOutput = pipe.Reader->GetOutputDataObject(0); - pipe.OutputDescription = vtkF3DGenericImporter::GetDataObjectDescription(readerOutput); - } - } + return this->Pimpl->ImportedImage; } diff --git a/vtkext/private/module/vtkF3DGenericImporter.h b/vtkext/private/module/vtkF3DGenericImporter.h index e7fff6d65b..2b1b7b2ed5 100644 --- a/vtkext/private/module/vtkF3DGenericImporter.h +++ b/vtkext/private/module/vtkF3DGenericImporter.h @@ -1,63 +1,37 @@ /** * @class vtkF3DGenericImporter - * @brief create a scene from the meta reader + * @brief create a scene for any vtkAlgorithm */ #ifndef vtkF3DGenericImporter_h #define vtkF3DGenericImporter_h #include "vtkF3DImporter.h" -#include "vtkF3DPostProcessFilter.h" -#include -#include -#include -#include -#include - -#include -#include #include -#include -#include -class vtkActor; -class vtkVolume; +class vtkAlgorithm; +class vtkDataObject; +class vtkImageData; class vtkMultiBlockDataSet; class vtkPartitionedDataSetCollection; -class vtkPointGaussianMapper; -class vtkPolyDataMapper; -class vtkSmartVolumeMapper; -class vtkTexture; - +class vtkPolyData; class vtkF3DGenericImporter : public vtkF3DImporter { public: static vtkF3DGenericImporter* New(); vtkTypeMacro(vtkF3DGenericImporter, vtkF3DImporter); - void PrintSelf(ostream& os, vtkIndent indent) override; - - /** - * Add an internal reader to generate actors from - */ - void AddInternalReader(const std::string& name, vtkAlgorithm* reader); /** - * Remove all internal readers + * Set the internal reader to recover actors and data from */ - void RemoveInternalReaders(); - - /** - * Check if the file can be read. - */ - bool CanReadFile(); + void SetInternalReader(vtkAlgorithm* reader); /** * Get a string describing the outputs */ std::string GetOutputsDescription() override; - std::string GetMetaDataDescription(); // TODO add to vtkImporter in VTK ? ///@{ /** @@ -67,59 +41,10 @@ class vtkF3DGenericImporter : public vtkF3DImporter static std::string GetPartitionedDataSetCollectionDescription( vtkPartitionedDataSetCollection* pdc, vtkIndent indent); static std::string GetDataObjectDescription(vtkDataObject* object); - static std::string GetMetaDataDescription(vtkDataObject* object); - ///@} - - ///@{ - /** - * Access to actors vectors. They all have the same size, which correspond to the number - * of added internal readers. - */ - std::vector> GetGeometryActorsAndMappers(); - std::vector> GetPointSpritesActorsAndMappers(); - std::vector> GetVolumePropsAndMappers(); ///@} /** - * A struct containing information about possible coloring - */ - struct ColoringInfo - { - std::string Name; - int MaximumNumberOfComponents = 0; - std::vector ComponentNames; - std::vector> ComponentRanges; - std::array MagnitudeRange = { std::numeric_limits::max(), - std::numeric_limits::min() }; - std::vector Arrays; - }; - - /** - * Recover information about coloring by index - * Should be called after actors have been imported - */ - bool GetInfoForColoring(bool useCellData, int index, ColoringInfo& info); - - /** - * Get the maximum index possible for coloring - * Should be called after actors have been imported - */ - int GetNumberOfIndexesForColoring(bool useCellData); - - /** - * Find an index for coloring corresponding to provided arrayName if available - * Should be called after actors have been imported - */ - int FindIndexForColoring(bool useCellData, const std::string& arrayName); - - /** - * Get the bounding box of all geometry actors - * Should be called after actors have been imported - */ - const vtkBoundingBox& GetGeometryBoundingBox(); - - /** - * Update readers and all pipelines on the specified timestep + * Update internal reader on the specified timestep */ bool UpdateAtTimeValue(double timeValue) override; @@ -131,7 +56,7 @@ class vtkF3DGenericImporter : public vtkF3DImporter vtkIdType GetNumberOfAnimations() override; /** - * Return a dummy name of the first animation if any, empty string otherwise. + * Return "default" for the first animation if any, empty string otherwise. */ std::string GetAnimationName(vtkIdType animationIndex) override; @@ -153,12 +78,20 @@ class vtkF3DGenericImporter : public vtkF3DImporter bool GetTemporalInformation(vtkIdType animationIndex, double frameRate, int& nbTimeSteps, double timeRange[2], vtkDoubleArray* timeSteps) override; + ///@{ + /** + * Direct access to generic importer specific datasets + */ + vtkPolyData* GetImportedPoints(); + vtkImageData* GetImportedImage(); + ///@} + protected: vtkF3DGenericImporter(); ~vtkF3DGenericImporter() override = default; - /* Standard ImportActors - * None of the actors are shown by default + /* + * Import surface from the internal reader output as actors */ void ImportActors(vtkRenderer*) override; @@ -167,12 +100,6 @@ class vtkF3DGenericImporter : public vtkF3DImporter */ void UpdateTemporalInformation(); - /** - * Update coloring information vectors according to - * currently added vectors - */ - void UpdateColoringVectors(bool useCellData); - /** * Update output descriptions according to current outputs */ diff --git a/vtkext/private/module/vtkF3DMetaImporter.cxx b/vtkext/private/module/vtkF3DMetaImporter.cxx new file mode 100644 index 0000000000..457db524e1 --- /dev/null +++ b/vtkext/private/module/vtkF3DMetaImporter.cxx @@ -0,0 +1,717 @@ +#include "vtkF3DMetaImporter.h" + +#include "F3DLog.h" +#include "vtkF3DGenericImporter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct vtkF3DMetaImporter::Internals +{ + void ClearColoringInfo() + { + this->PointDataColoringInfo.clear(); + this->CellDataColoringInfo.clear(); + } + + void UpdateColoringInfo(vtkDataSet* dataset, bool useCellData) + { + // XXX: This assumes importer do not import actors with an empty input + assert(dataset); + + // Recover all possible names + std::set arrayNames; + + vtkDataSetAttributes* attr = useCellData + ? static_cast(dataset->GetCellData()) + : static_cast(dataset->GetPointData()); + + for (int i = 0; i < attr->GetNumberOfArrays(); i++) + { + vtkDataArray* array = attr->GetArray(i); + if (array && array->GetName()) + { + arrayNames.insert(array->GetName()); + } + } + + auto& data = useCellData ? this->CellDataColoringInfo : this->PointDataColoringInfo; + + for (const std::string& arrayName : arrayNames) + { + // Recover/Create a coloring info + vtkF3DMetaImporter::ColoringInfo& info = data[arrayName]; + info.Name = arrayName; + + vtkDataArray* array = useCellData ? dataset->GetCellData()->GetArray(arrayName.c_str()) + : dataset->GetPointData()->GetArray(arrayName.c_str()); + if (array) + { + info.MaximumNumberOfComponents = + std::max(info.MaximumNumberOfComponents, array->GetNumberOfComponents()); + + // Set ranges + // XXX this does not take animation into account + std::array range; + array->GetRange(range.data(), -1); + info.MagnitudeRange[0] = std::min(info.MagnitudeRange[0], range[0]); + info.MagnitudeRange[1] = std::max(info.MagnitudeRange[1], range[1]); + + for (size_t i = 0; i < static_cast(array->GetNumberOfComponents()); i++) + { + array->GetRange(range.data(), static_cast(i)); + if (i < info.ComponentRanges.size()) + { + info.ComponentRanges[i][0] = std::min(info.ComponentRanges[i][0], range[0]); + info.ComponentRanges[i][1] = std::max(info.ComponentRanges[i][1], range[1]); + } + else + { + info.ComponentRanges.emplace_back(range); + } + } + + // Set component names + if (array->HasAComponentName()) + { + for (size_t i = 0; i < static_cast(array->GetNumberOfComponents()); i++) + { + const char* compName = array->GetComponentName(i); + if (i < info.ComponentNames.size()) + { + if (compName && info.ComponentNames[i] != std::string(compName)) + { + // set non-coherent component names to empty string + info.ComponentNames[i] = ""; + } + } + else + { + // Add components names to the back of the component names vector + info.ComponentNames.emplace_back(compName ? compName : ""); + } + } + } + } + } + } + + void FinalizeColoringInfo(bool useCellData) + { + auto& names = useCellData ? this->CellDataArrayNames : this->PointDataArrayNames; + names.clear(); + + auto& data = useCellData ? this->CellDataColoringInfo : this->PointDataColoringInfo; + int index = 0; + for (auto& [name, info] : data) + { + info.Index = index; + names.emplace_back(name); + index++; + } + } + + // Actors related vectors + std::vector ColoringActorsAndMappers; + std::vector PointSpritesActorsAndMappers; + std::vector VolumePropsAndMappers; + + // Map of arrayName -> coloring info + std::map PointDataColoringInfo; + std::map CellDataColoringInfo; + std::vector PointDataArrayNames; + std::vector CellDataArrayNames; + + struct ImporterPair + { + vtkSmartPointer Importer; + bool Updated = false; + }; + std::vector Importers; + std::optional CameraIndex; + vtkBoundingBox GeometryBoundingBox; + bool ColoringInfoUpdated = false; + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 3, 20240707) + std::map> ActorsForImporterMap; +#endif +}; + +vtkStandardNewMacro(vtkF3DMetaImporter); + +//---------------------------------------------------------------------------- +vtkF3DMetaImporter::vtkF3DMetaImporter() + : Pimpl(new Internals()) +{ +} + +//---------------------------------------------------------------------------- +vtkF3DMetaImporter::~vtkF3DMetaImporter() +{ + // XXX by doing this we ensure ~vtkImporter does not delete it + // As we have our own way of handling renderer lifetime + this->Renderer = nullptr; +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::Clear() +{ + this->Pimpl->Importers.clear(); + this->Pimpl->GeometryBoundingBox.Reset(); + this->ActorCollection->RemoveAllItems(); + this->Pimpl->ColoringActorsAndMappers.clear(); + this->Pimpl->PointSpritesActorsAndMappers.clear(); + this->Pimpl->VolumePropsAndMappers.clear(); + this->Pimpl->ClearColoringInfo(); + this->Modified(); +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::AddImporter(const vtkSmartPointer& importer) +{ + this->Pimpl->Importers.emplace_back(vtkF3DMetaImporter::Internals::ImporterPair {importer, false}); + this->Modified(); + + // Add a progress event observer + vtkNew progressCallback; + progressCallback->SetClientData(this); + progressCallback->SetCallback( + [](vtkObject* const caller, unsigned long, void* clientData, void* callData) + { + vtkF3DMetaImporter* self = static_cast(clientData); + double progress = *static_cast(callData); + double actualProgress = 0.0; + for (size_t i = 0; i < self->Pimpl->Importers.size(); i++) + { + if (self->Pimpl->Importers[i].Importer == caller) + { + // XXX: This does not consider that some importer may already have been updated + // or that some importers may take much longer than other. + actualProgress = (i + progress) / self->Pimpl->Importers.size(); + } + } + self->InvokeEvent(vtkCommand::ProgressEvent, &actualProgress); + }); + importer->AddObserver(vtkCommand::ProgressEvent, progressCallback); +} + +//---------------------------------------------------------------------------- +const vtkBoundingBox& vtkF3DMetaImporter::GetGeometryBoundingBox() +{ + return this->Pimpl->GeometryBoundingBox; +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::GetInfoForColoring( + bool useCellData, int index, vtkF3DMetaImporter::ColoringInfo& info) +{ + if (!this->Pimpl->ColoringInfoUpdated) + { + this->UpdateInfoForColoring(); + } + + auto& data = + useCellData ? this->Pimpl->CellDataColoringInfo : this->Pimpl->PointDataColoringInfo; + auto& names = + useCellData ? this->Pimpl->CellDataArrayNames : this->Pimpl->PointDataArrayNames; + + if (index < 0 || index >= static_cast(data.size())) + { + return false; + } + + info = data[names[index]]; + return true; +} + +//---------------------------------------------------------------------------- +int vtkF3DMetaImporter::GetNumberOfIndexesForColoring(bool useCellData) +{ + if (!this->Pimpl->ColoringInfoUpdated) + { + this->UpdateInfoForColoring(); + } + + auto& data = + useCellData ? this->Pimpl->CellDataColoringInfo : this->Pimpl->PointDataColoringInfo; + return static_cast(data.size()); +} + +//---------------------------------------------------------------------------- +int vtkF3DMetaImporter::FindIndexForColoring(bool useCellData, const std::string& arrayName) +{ + if (!this->Pimpl->ColoringInfoUpdated) + { + this->UpdateInfoForColoring(); + } + + auto& data = + useCellData ? this->Pimpl->CellDataColoringInfo : this->Pimpl->PointDataColoringInfo; + + auto it = data.find(arrayName); + if (it != data.end()) + { + return it->second.Index; + } + return -1; +} + +//---------------------------------------------------------------------------- +const std::vector& +vtkF3DMetaImporter::GetColoringActorsAndMappers() +{ + return this->Pimpl->ColoringActorsAndMappers; +} + +//---------------------------------------------------------------------------- +const std::vector& +vtkF3DMetaImporter::GetPointSpritesActorsAndMappers() +{ + return this->Pimpl->PointSpritesActorsAndMappers; +} + +//---------------------------------------------------------------------------- +const std::vector& vtkF3DMetaImporter::GetVolumePropsAndMappers() +{ + return this->Pimpl->VolumePropsAndMappers; +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::Update() +{ + assert(this->RenderWindow); + this->Renderer = this->RenderWindow->GetRenderers()->GetFirstRenderer(); + assert(this->Renderer); + + // XXX: Doing this means that coloring information + // is recomputed from scratch when doing incremental loading + // This is the simplest way to implement coloring + // and should not have too big of an overhead. + this->Pimpl->ColoringInfoUpdated = false; + + vtkIdType localCameraIndex = -1; + + if (this->Pimpl->CameraIndex.has_value()) + { + if (this->Pimpl->CameraIndex < 0) + { + F3DLog::Print(F3DLog::Severity::Warning, + "Invalid camera index: " + std::to_string(this->Pimpl->CameraIndex.value()) + + ". Camera may be incorrect."); + } + localCameraIndex = this->Pimpl->CameraIndex.value(); + } + + for (auto& importerPair : this->Pimpl->Importers) + { + vtkImporter* importer = importerPair.Importer; + + // Importer has already been updated + if (importerPair.Updated) + { + localCameraIndex -= importer->GetNumberOfCameras(); + continue; + } + + importer->SetRenderWindow(this->RenderWindow); + + // This is required to avoid updating two times + // but may cause a warning in VTK + if (localCameraIndex >= 0) + { + importer->SetCamera(localCameraIndex); + } + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + if (!importer->Update()) + { + return false; + } +#else + vtkSmartPointer actorCollection = + vtkSmartPointer::New(); + + vtkNew previousActorCollection; + vtkActorCollection* currentCollection = this->Renderer->GetActors(); + vtkCollectionSimpleIterator tmpIt; + currentCollection->InitTraversal(tmpIt); + while (auto* actor = currentCollection->GetNextActor(tmpIt)) + { + previousActorCollection->AddItem(actor); + } + + importer->Update(); + + currentCollection = this->Renderer->GetActors(); + currentCollection->InitTraversal(tmpIt); + + vtkCollectionSimpleIterator tmpIt2; + previousActorCollection->InitTraversal(tmpIt2); + while (auto* actor = currentCollection->GetNextActor(tmpIt)) + { + bool found = false; + while (auto* previousActor = previousActorCollection->GetNextActor(tmpIt2)) + { + // This is a N^2 loop + if (previousActor == actor) + { + found = true; + break; + } + } + if (!found) + { + actorCollection->AddItem(actor); + } + } + + // Store the actor collection for further use + this->Pimpl->ActorsForImporterMap[importer] = actorCollection; +#endif + + localCameraIndex -= importer->GetNumberOfCameras(); + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + vtkActorCollection* actorCollection = importer->GetImportedActors(); +#endif + vtkCollectionSimpleIterator ait; + actorCollection->InitTraversal(ait); + while (auto* actor = actorCollection->GetNextActor(ait)) + { + // Add to the actor collection + this->ActorCollection->AddItem(actor); + + vtkPolyDataMapper* pdMapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + vtkPolyData* surface = pdMapper->GetInput(); + + // Increase bounding box size if needed + double bounds[6]; + surface->GetBounds(bounds); + this->Pimpl->GeometryBoundingBox.AddBounds(bounds); + + // Recover generic importer if any + vtkF3DGenericImporter* genericImporter = vtkF3DGenericImporter::SafeDownCast(importer); + + // Create and configure coloring actors + this->Pimpl->ColoringActorsAndMappers.emplace_back(vtkF3DMetaImporter::ColoringStruct(actor)); + vtkF3DMetaImporter::ColoringStruct& cs = this->Pimpl->ColoringActorsAndMappers.back(); + cs.Mapper->SetInputData(surface); + this->Renderer->AddActor(cs.Actor); + cs.Actor->VisibilityOff(); + + // Create and configure point sprites actors + this->Pimpl->PointSpritesActorsAndMappers.emplace_back( + vtkF3DMetaImporter::PointSpritesStruct()); + vtkF3DMetaImporter::PointSpritesStruct& pss = + this->Pimpl->PointSpritesActorsAndMappers.back(); + + vtkPolyData* points = surface; + if (genericImporter) + { + // For generic importer, use the single imported points + // TODO when supporting composite, handle with an actor based index + points = genericImporter->GetImportedPoints(); + } + pss.Mapper->SetInputData(points); + this->Renderer->AddActor(pss.Actor); + pss.Actor->VisibilityOff(); + + // Create and configure volume props + if (genericImporter) + { + vtkImageData* image = genericImporter->GetImportedImage(); + if (image) + { + // XXX: Note that creating this struct takes some time + this->Pimpl->VolumePropsAndMappers.emplace_back(vtkF3DMetaImporter::VolumeStruct()); + vtkF3DMetaImporter::VolumeStruct& vs = this->Pimpl->VolumePropsAndMappers.back(); + vs.Mapper->SetInputData(image); + this->Renderer->AddVolume(vs.Prop); + vs.Prop->VisibilityOff(); + } + } + } + + importerPair.Updated = true; + } + + if (localCameraIndex > 0) + { + // Here we know that CameraIndex has a value + F3DLog::Print(F3DLog::Severity::Warning, + "Camera index " + std::to_string(this->Pimpl->CameraIndex.value()) + + " is higher than the number of available camera in the files. Camera may be incorrect."); + } + + // XXX: UpdateStatus is not set, but libf3d does not use it + return true; +} + +//---------------------------------------------------------------------------- +std::string vtkF3DMetaImporter::GetOutputsDescription() +{ + std::string description = "Number of files: " + std::to_string(this->Pimpl->Importers.size()) + "\n"; + description += "Number of actors: " + std::to_string(this->ActorCollection->GetNumberOfItems()) + "\n"; + description += std::accumulate(this->Pimpl->Importers.begin(), this->Pimpl->Importers.end(), + std::string(), [](const std::string& a, const auto& importerPair) + { return a + "----------\n" + importerPair.Importer->GetOutputsDescription(); }); + return description; +} + +//---------------------------------------------------------------------------- +vtkIdType vtkF3DMetaImporter::GetNumberOfAnimations() +{ + return std::accumulate(this->Pimpl->Importers.begin(), this->Pimpl->Importers.end(), 0, + [](vtkIdType a, const auto& importerPair) + { return a + importerPair.Importer->GetNumberOfAnimations(); }); +} + +//---------------------------------------------------------------------------- +std::string vtkF3DMetaImporter::GetAnimationName(vtkIdType animationIndex) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + std::string name = importerPair.Importer->GetAnimationName(localAnimationIndex); + if (name.empty()) + { + name = "unnamed_" + std::to_string(animationIndex); + } + return name; + } + else + { + localAnimationIndex -= nAnim; + } + } + return ""; +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::EnableAnimation(vtkIdType animationIndex) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + importerPair.Importer->EnableAnimation(localAnimationIndex); + return; + } + else + { + localAnimationIndex -= nAnim; + } + } +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::DisableAnimation(vtkIdType animationIndex) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + importerPair.Importer->DisableAnimation(localAnimationIndex); + return; + } + else + { + localAnimationIndex -= nAnim; + } + } +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::IsAnimationEnabled(vtkIdType animationIndex) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + return importerPair.Importer->IsAnimationEnabled(localAnimationIndex); + } + else + { + localAnimationIndex -= nAnim; + } + } + return false; +} + +//---------------------------------------------------------------------------- +vtkIdType vtkF3DMetaImporter::GetNumberOfCameras() +{ + return std::accumulate(this->Pimpl->Importers.begin(), this->Pimpl->Importers.end(), 0, + [](vtkIdType a, const auto& importerPair) + { return a + importerPair.Importer->GetNumberOfCameras(); }); +} + +//---------------------------------------------------------------------------- +std::string vtkF3DMetaImporter::GetCameraName(vtkIdType camIndex) +{ + vtkIdType localCameraIndex = camIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nCam = importerPair.Importer->GetNumberOfCameras(); + if (localCameraIndex < nCam) + { + std::string name = importerPair.Importer->GetCameraName(localCameraIndex); + if (name.empty()) + { + name = "unnamed_" + std::to_string(camIndex); + } + return name; + } + else + { + localCameraIndex -= nCam; + } + } + return ""; +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::SetCameraIndex(std::optional camIndex) +{ + this->Pimpl->CameraIndex = camIndex; +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::GetTemporalInformation(vtkIdType animationIndex, double frameRate, + int& nbTimeSteps, double timeRange[2], vtkDoubleArray* timeSteps) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + return importerPair.Importer->GetTemporalInformation( + localAnimationIndex, frameRate, nbTimeSteps, timeRange, timeSteps); + } + else + { + localAnimationIndex -= nAnim; + } + } + return false; +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::UpdateAtTimeValue(double timeValue) +{ + this->Pimpl->ColoringInfoUpdated = false; + bool ret = true; + for (const auto& importerPair : this->Pimpl->Importers) + { +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + ret = ret && importerPair.Importer->UpdateAtTimeValue(timeValue); +#else + importerPair.Importer->UpdateTimeStep(timeValue); +#endif + } + return ret; +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::UpdateInfoForColoring() +{ + for (const auto& importerPair : this->Pimpl->Importers) + { +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + vtkActorCollection* actorCollection = importerPair.Importer->GetImportedActors(); +#else + vtkActorCollection* actorCollection = + this->Pimpl->ActorsForImporterMap.at(importerPair.Importer).Get(); +#endif + vtkCollectionSimpleIterator ait; + actorCollection->InitTraversal(ait); + while (auto* actor = actorCollection->GetNextActor(ait)) + { + vtkPolyDataMapper* pdMapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + assert(pdMapper); + + // Update coloring vectors, with a dedicated logic for generic importer + vtkDataSet* datasetForColoring = pdMapper->GetInput(); + vtkF3DGenericImporter* genericImporter = vtkF3DGenericImporter::SafeDownCast(importerPair.Importer); + if (genericImporter) + { + // TODO This will be improved with proper composite support + // Currently generic importer always has a single actor + if (genericImporter->GetImportedImage()) + { + datasetForColoring = genericImporter->GetImportedImage(); + } + else if (genericImporter->GetImportedPoints()) + { + datasetForColoring = genericImporter->GetImportedPoints(); + } + } + this->Pimpl->UpdateColoringInfo(datasetForColoring, false); + this->Pimpl->UpdateColoringInfo(datasetForColoring, true); + } + } + + this->Pimpl->FinalizeColoringInfo(false); + this->Pimpl->FinalizeColoringInfo(true); + this->Pimpl->ColoringInfoUpdated = true; +} + +//---------------------------------------------------------------------------- +std::string vtkF3DMetaImporter::GetMetaDataDescription() const +{ + std::string description; + if (this->Pimpl->Importers.size() > 1) + { + description += "Number of files: "; + description += std::to_string(this->Pimpl->Importers.size()); + description += "\n"; + } + + description += "Number of actors: "; + description += std::to_string(this->ActorCollection->GetNumberOfItems()); + description += "\n"; + + vtkIdType nPoints = 0; + vtkIdType nCells = 0; + vtkCollectionSimpleIterator ait; + this->ActorCollection->InitTraversal(ait); + while (auto* actor = this->ActorCollection->GetNextActor(ait)) + { + vtkPolyData* surface = vtkPolyDataMapper::SafeDownCast(actor->GetMapper())->GetInput(); + nPoints += surface->GetNumberOfPoints(); + nCells += surface->GetNumberOfCells(); + } + + description += "Number of points: "; + description += std::to_string(nPoints); + description += "\n"; + description += "Number of cells: "; + description += std::to_string(nCells); + return description; +} diff --git a/vtkext/private/module/vtkF3DMetaImporter.h b/vtkext/private/module/vtkF3DMetaImporter.h new file mode 100644 index 0000000000..37b2ee4999 --- /dev/null +++ b/vtkext/private/module/vtkF3DMetaImporter.h @@ -0,0 +1,213 @@ +/** + * @class vtkF3DMetaImporter + * @brief + */ + +#ifndef vtkF3DMetaImporter_h +#define vtkF3DMetaImporter_h + +#include "vtkF3DImporter.h" + +#include +#include +#include +#include +#include +#include +#include + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 3, 20240707) +#include +#endif + +#include +#include +#include +#include +#include +#include + +class vtkF3DMetaImporter : public vtkF3DImporter +{ +public: + static vtkF3DMetaImporter* New(); + vtkTypeMacro(vtkF3DMetaImporter, vtkF3DImporter); + + ///@{ + /** + * Structs used to transfer actors information to the F3D renderer + */ + struct VolumeStruct + { + VolumeStruct() + { + this->Mapper->SetRequestedRenderModeToGPU(); + this->Prop->SetMapper(this->Mapper); + } + vtkNew Prop; + vtkNew Mapper; + }; + + struct PointSpritesStruct + { + PointSpritesStruct() + { + this->Actor->SetMapper(this->Mapper); + } + + vtkNew Actor; + vtkNew Mapper; + }; + + struct ColoringStruct + { + explicit ColoringStruct(vtkActor* originalActor) + : OriginalActor(originalActor) + { + this->Actor->GetProperty()->SetPointSize(10.0); + this->Actor->GetProperty()->SetLineWidth(1.0); + this->Actor->GetProperty()->SetRoughness(0.3); + this->Actor->GetProperty()->SetInterpolationToPBR(); + this->Actor->SetMapper(this->Mapper); + this->Mapper->InterpolateScalarsBeforeMappingOn(); + } + vtkNew Actor; + vtkNew Mapper; + vtkActor* OriginalActor; + }; + ///@} + + /** + * A struct containing information about possible coloring + */ + struct ColoringInfo + { + std::string Name; + int MaximumNumberOfComponents = 0; + std::vector ComponentNames; + std::vector> ComponentRanges; + std::array MagnitudeRange = { std::numeric_limits::max(), + std::numeric_limits::min() }; + int Index = -1; + }; + + /** + * Clear all importers and internal structures + */ + void Clear(); + + /** + * Add an importer to update when importer all actors + */ + void AddImporter(const vtkSmartPointer& importer); + + /** + * Get the bounding box of all geometry actors + * Should be called after actors have been imported + */ + const vtkBoundingBox& GetGeometryBoundingBox(); + + /** + * Get a meta data description of all imported data + */ + std::string GetMetaDataDescription() const; + + /** + * Recover information about coloring by index + * Should be called after actors have been imported + */ + bool GetInfoForColoring(bool useCellData, int index, ColoringInfo& info); + + /** + * Get the maximum index possible for coloring + * Should be called after actors have been imported + */ + int GetNumberOfIndexesForColoring(bool useCellData); + + /** + * Find an index for coloring corresponding to provided arrayName if available + * Should be called after actors have been imported + */ + int FindIndexForColoring(bool useCellData, const std::string& arrayName); + + ///@{ + /** + * API to recover information about all imported actors, point sprites and volume if any + */ + const std::vector& GetColoringActorsAndMappers(); + const std::vector& GetPointSpritesActorsAndMappers(); + const std::vector& GetVolumePropsAndMappers(); + ///@} + + /** + * XXX: HIDE the vtkImporter::Update method and declare our own + * Import each of of the add importers into the first renderer of the render window. + * Importers that have already been imported will be skipped + * Also handles camera index if specified + * After import, create point sprites actors for all importers, and volume props + * for generic importer if compatible. + */ + bool Update(); + + /** + * Concatenate individual importers output description into one and return it + */ + std::string GetOutputsDescription() override; + + ///@{ + /** + * Implement vtkImporter animation API by adding animations for each individual importers one after the other + * No input checking on animationIndex + */ + vtkIdType GetNumberOfAnimations() override; + std::string GetAnimationName(vtkIdType animationIndex) override; + void EnableAnimation(vtkIdType animationIndex) override; + void DisableAnimation(vtkIdType animationIndex) override; + bool IsAnimationEnabled(vtkIdType animationIndex) override; + bool GetTemporalInformation(vtkIdType animationIndex, double frameRate, int& nbTimeSteps, double timeRange[2], vtkDoubleArray* timeSteps) override; + ///@} + + ///@{ + /** + * Implement vtkImporter camera API by adding cameras for each individual importers one after the other + * No input checking on camIndex + * Please note `void SetCamera(vtkIdType camIndex);` is not reimplemented nor used. + */ + vtkIdType GetNumberOfCameras() override; + std::string GetCameraName(vtkIdType camIndex) override; + void SetCameraIndex(std::optional camIndex); + ///@} + + /** + * Update each individual importer at the provided value + */ + bool UpdateAtTimeValue(double timeValue) override; + +protected: + vtkF3DMetaImporter(); + ~vtkF3DMetaImporter() override; + +private: + vtkF3DMetaImporter(const vtkF3DMetaImporter&) = delete; + void operator=(const vtkF3DMetaImporter&) = delete; + + /** + * Hide vtkImporter::SetCamera to ensure it is not being used + */ + using vtkImporter::SetCamera; + + /** + * Recover coloring information from each individual importer + * and store result in internal fields + */ + void UpdateInfoForColoring(); + + struct Internals; + std::unique_ptr Pimpl; + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 3, 20240707) + vtkNew ActorCollection; +#endif +}; + +#endif diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 4006db417d..8673b061b2 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -177,6 +177,9 @@ vtkF3DRenderer::vtkF3DRenderer() this->CheatSheetActor->VisibilityOff(); this->DropZoneActor->VisibilityOff(); this->SkyboxActor->VisibilityOff(); + + // Make sure an active camera is available on the renderer + this->GetActiveCamera(); } //---------------------------------------------------------------------------- @@ -194,7 +197,7 @@ void vtkF3DRenderer::ReleaseGraphicsResources(vtkWindow* w) } //---------------------------------------------------------------------------- -void vtkF3DRenderer::Initialize(const std::string& up) +void vtkF3DRenderer::Initialize() { this->OriginalLightIntensities.clear(); this->RemoveAllViewProps(); @@ -223,11 +226,14 @@ void vtkF3DRenderer::Initialize(const std::string& up) this->AnimationNameInfo = ""; this->GridInfo = ""; +} - // Importer rely on the Environment being set, so this is needed in the initialization +//---------------------------------------------------------------------------- +void vtkF3DRenderer::InitializeUpVector(const std::string& upString) +{ const std::regex re("([-+]?)([XYZ])", std::regex_constants::icase); std::smatch match; - if (std::regex_match(up, match, re)) + if (std::regex_match(upString, match, re)) { const float sign = match[1].str() == "-" ? -1.0 : +1.0; const int index = std::toupper(match[2].str()[0]) - 'X'; @@ -245,6 +251,8 @@ void vtkF3DRenderer::Initialize(const std::string& up) vtkMath::Cross(this->UpVector, this->RightVector, pos); vtkMath::MultiplyScalar(pos, -1.0); + // XXX: Initialize the camera to a default position + // Note that camera reset is expected to be called later during importing vtkCamera* cam = this->GetActiveCamera(); cam->SetFocalPoint(0.0, 0.0, 0.0); cam->SetPosition(pos); @@ -262,7 +270,7 @@ void vtkF3DRenderer::Initialize(const std::string& up) } else { - F3DLog::Print(F3DLog::Severity::Warning, up + " is not a valid up direction"); + F3DLog::Print(F3DLog::Severity::Warning, upString + " is not a valid up direction"); } } @@ -1383,6 +1391,7 @@ void vtkF3DRenderer::FillCheatSheetHotkeys(std::stringstream& cheatSheetText) //---------------------------------------------------------------------------- void vtkF3DRenderer::ConfigureActorsProperties() { + // XXX should be handled using new importer API once renderers are merged vtkActor* anActor; vtkActorCollection* ac = this->GetActors(); vtkCollectionSimpleIterator ait; diff --git a/vtkext/private/module/vtkF3DRenderer.h b/vtkext/private/module/vtkF3DRenderer.h index c76f9de57c..cc2cc5dae3 100644 --- a/vtkext/private/module/vtkF3DRenderer.h +++ b/vtkext/private/module/vtkF3DRenderer.h @@ -82,11 +82,12 @@ class vtkF3DRenderer : public vtkOpenGLRenderer void SetFinalShader(const std::optional& finalShader); ///@} - ///@{ /** * Set SetUseOrthographicProjection */ void SetUseOrthographicProjection(bool use); + + ///@{ /** * Set/Get UseTrackball */ @@ -132,7 +133,12 @@ class vtkF3DRenderer : public vtkOpenGLRenderer * Initialize the renderer actors and flags. * Should be called after being added to a vtkRenderWindow. */ - virtual void Initialize(const std::string& up); + virtual void Initialize(); + + /** + * Initialize actors properties related to the up vector using the provided upString, including the camera + */ + void InitializeUpVector(const std::string& upString); /** * Get the OpenGL skybox @@ -244,8 +250,6 @@ class vtkF3DRenderer : public vtkOpenGLRenderer */ static std::string ShortName(const std::string& name, int maxChar); - vtkNew InitialCamera; - vtkSmartPointer AxisWidget; vtkNew FilenameActor; @@ -259,6 +263,7 @@ class vtkF3DRenderer : public vtkOpenGLRenderer vtkNew TimerActor; unsigned int Timer = 0; + bool UpVectorConfigured = false; bool CheatSheetConfigured = false; bool ActorsPropertiesConfigured = false; bool GridConfigured = false; diff --git a/vtkext/private/module/vtkF3DRendererWithColoring.cxx b/vtkext/private/module/vtkF3DRendererWithColoring.cxx index 0b06bdf75e..5458f405f0 100644 --- a/vtkext/private/module/vtkF3DRendererWithColoring.cxx +++ b/vtkext/private/module/vtkF3DRendererWithColoring.cxx @@ -2,7 +2,7 @@ #include "F3DLog.h" #include "vtkF3DConfigure.h" -#include "vtkF3DGenericImporter.h" +#include "vtkF3DMetaImporter.h" #include #include @@ -21,6 +21,10 @@ #include #include +#include +#include +#include + #include #include @@ -70,17 +74,16 @@ vtkSmartPointer GetTexture(const std::string& filePath, bool isSRGB vtkStandardNewMacro(vtkF3DRendererWithColoring); //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetImporter(vtkF3DGenericImporter* importer) +void vtkF3DRendererWithColoring::SetImporter(vtkF3DMetaImporter* importer) { this->Importer = importer; - this->Modified(); } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::Initialize(const std::string& up) +void vtkF3DRendererWithColoring::Initialize() { // This remove all actors and view props - this->Superclass::Initialize(up); + this->Superclass::Initialize(); this->ArrayIndexForColoring = -1; this->ComponentForColoring = -1; @@ -89,7 +92,7 @@ void vtkF3DRendererWithColoring::Initialize(const std::string& up) this->ScalarBarActor->VisibilityOff(); this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; + this->ColoringMappersConfigured = false; this->PointSpritesMappersConfigured = false; this->VolumePropsAndMappersConfigured = false; this->ScalarBarActorConfigured = false; @@ -99,7 +102,7 @@ void vtkF3DRendererWithColoring::Initialize(const std::string& up) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetRoughness(double roughness) +void vtkF3DRendererWithColoring::SetRoughness(const std::optional& roughness) { if (this->Roughness != roughness) { @@ -109,7 +112,7 @@ void vtkF3DRendererWithColoring::SetRoughness(double roughness) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetOpacity(double opacity) +void vtkF3DRendererWithColoring::SetOpacity(const std::optional& opacity) { if (this->Opacity != opacity) { @@ -119,7 +122,7 @@ void vtkF3DRendererWithColoring::SetOpacity(double opacity) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetMetallic(double metallic) +void vtkF3DRendererWithColoring::SetMetallic(const std::optional& metallic) { if (this->Metallic != metallic) { @@ -129,7 +132,7 @@ void vtkF3DRendererWithColoring::SetMetallic(double metallic) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetNormalScale(double normalScale) +void vtkF3DRendererWithColoring::SetNormalScale(const std::optional& normalScale) { if (this->NormalScale != normalScale) { @@ -139,27 +142,27 @@ void vtkF3DRendererWithColoring::SetNormalScale(double normalScale) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetSurfaceColor(double* color) +void vtkF3DRendererWithColoring::SetSurfaceColor(const std::optional>& color) { - if (!std::equal(color, color + 3, this->SurfaceColor)) + if (this->SurfaceColor != color) { - std::copy(color, color + 3, this->SurfaceColor); + this->SurfaceColor = color; this->ColoringActorsPropertiesConfigured = false; } } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetEmissiveFactor(double* factor) +void vtkF3DRendererWithColoring::SetEmissiveFactor(const std::optional>& factor) { - if (!std::equal(factor, factor + 3, this->EmissiveFactor)) + if (this->EmissiveFactor != factor) { - std::copy(factor, factor + 3, this->EmissiveFactor); + this->EmissiveFactor = factor; this->ColoringActorsPropertiesConfigured = false; } } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureMatCap(const std::string& tex) +void vtkF3DRendererWithColoring::SetTextureMatCap(const std::optional& tex) { if (this->TextureMatCap != tex) { @@ -169,7 +172,7 @@ void vtkF3DRendererWithColoring::SetTextureMatCap(const std::string& tex) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureBaseColor(const std::string& tex) +void vtkF3DRendererWithColoring::SetTextureBaseColor(const std::optional& tex) { if (this->TextureBaseColor != tex) { @@ -179,7 +182,7 @@ void vtkF3DRendererWithColoring::SetTextureBaseColor(const std::string& tex) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureMaterial(const std::string& tex) +void vtkF3DRendererWithColoring::SetTextureMaterial(const std::optional& tex) { if (this->TextureMaterial != tex) { @@ -189,7 +192,7 @@ void vtkF3DRendererWithColoring::SetTextureMaterial(const std::string& tex) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureEmissive(const std::string& tex) +void vtkF3DRendererWithColoring::SetTextureEmissive(const std::optional& tex) { if (this->TextureEmissive != tex) { @@ -199,7 +202,7 @@ void vtkF3DRendererWithColoring::SetTextureEmissive(const std::string& tex) } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureNormal(const std::string& tex) +void vtkF3DRendererWithColoring::SetTextureNormal(const std::optional& tex) { if (this->TextureNormal != tex) { @@ -213,37 +216,127 @@ void vtkF3DRendererWithColoring::ConfigureColoringActorsProperties() { assert(this->Importer); - for (auto& actorAndMapper : this->Importer->GetGeometryActorsAndMappers()) + double* surfaceColor = nullptr; + if (this->SurfaceColor.has_value()) { - // XXX: All properties below should be optional once we can apply them - // on full scenes importers - actorAndMapper.first->GetProperty()->SetColor(this->SurfaceColor); - actorAndMapper.first->GetProperty()->SetOpacity(this->Opacity); - actorAndMapper.first->GetProperty()->SetRoughness(this->Roughness); - actorAndMapper.first->GetProperty()->SetMetallic(this->Metallic); + if (this->SurfaceColor.value().size() != 3) + { + F3DLog::Print(F3DLog::Severity::Warning, + std::string("Invalid surface color provided, not applying\n")); + } + else + { + surfaceColor = this->SurfaceColor.value().data(); + } + } + + double* emissiveFactor = nullptr; + if (this->EmissiveFactor.has_value()) + { + if (this->EmissiveFactor.value().size() != 3) + { + F3DLog::Print(F3DLog::Severity::Warning, + std::string("Invalid emissive factor provided, not applying\n")); + } + else + { + emissiveFactor = this->EmissiveFactor.value().data(); + } + } + + for ([[maybe_unused]] const auto& [actor, mapper, originalActor] : this->Importer->GetColoringActorsAndMappers()) + { + if(surfaceColor) + { + actor->GetProperty()->SetColor(surfaceColor); + originalActor->GetProperty()->SetColor(surfaceColor); + } + + if (this->Opacity.has_value()) + { + actor->GetProperty()->SetOpacity(this->Opacity.value()); + originalActor->GetProperty()->SetOpacity(this->Opacity.value()); + } + + if (this->Roughness.has_value()) + { + actor->GetProperty()->SetRoughness(this->Roughness.value()); + originalActor->GetProperty()->SetRoughness(this->Roughness.value()); + } + + if (this->Metallic.has_value()) + { + actor->GetProperty()->SetMetallic(this->Metallic.value()); + originalActor->GetProperty()->SetMetallic(this->Metallic.value()); + } // Textures - auto colorTex = ::GetTexture(this->TextureBaseColor, true); - actorAndMapper.first->GetProperty()->SetBaseColorTexture(colorTex); - actorAndMapper.first->GetProperty()->SetORMTexture(::GetTexture(this->TextureMaterial)); - actorAndMapper.first->GetProperty()->SetEmissiveTexture( - ::GetTexture(this->TextureEmissive, true)); - actorAndMapper.first->GetProperty()->SetEmissiveFactor(this->EmissiveFactor); - actorAndMapper.first->GetProperty()->SetNormalTexture(::GetTexture(this->TextureNormal)); - actorAndMapper.first->GetProperty()->SetNormalScale(this->NormalScale); - actorAndMapper.first->GetProperty()->SetTexture("matcap", ::GetTexture(this->TextureMatCap)); - - // If the input texture is RGBA, flag the actor as translucent - if (colorTex && colorTex->GetImageDataInput(0)->GetNumberOfScalarComponents() == 4) + if (this->TextureBaseColor.has_value()) + { + auto colorTex = ::GetTexture(this->TextureBaseColor.value(), true); + actor->GetProperty()->SetBaseColorTexture(colorTex); + originalActor->GetProperty()->SetBaseColorTexture(colorTex); + + // If the input texture is RGBA, flag the actor as translucent + if (colorTex && colorTex->GetImageDataInput(0)->GetNumberOfScalarComponents() == 4) + { + actor->ForceTranslucentOn(); + originalActor->ForceTranslucentOn(); + } + } + + if (this->TextureMaterial.has_value()) { - actorAndMapper.first->ForceTranslucentOn(); + auto matTex = ::GetTexture(this->TextureMaterial.value()); + actor->GetProperty()->SetORMTexture(matTex); + originalActor->GetProperty()->SetORMTexture(matTex); + } + + if (this->TextureEmissive.has_value()) + { + auto emissTex = ::GetTexture(this->TextureEmissive.value(), true); + actor->GetProperty()->SetEmissiveTexture(emissTex); + originalActor->GetProperty()->SetEmissiveTexture(emissTex); + } + + if (emissiveFactor) + { + actor->GetProperty()->SetEmissiveFactor(emissiveFactor); + originalActor->GetProperty()->SetEmissiveFactor(emissiveFactor); + } + + if (this->TextureNormal.has_value()) + { + auto normTex = ::GetTexture(this->TextureNormal.value()); + actor->GetProperty()->SetNormalTexture(normTex); + originalActor->GetProperty()->SetNormalTexture(normTex); + } + + if (this->NormalScale.has_value()) + { + actor->GetProperty()->SetNormalScale(this->NormalScale.value()); + originalActor->GetProperty()->SetNormalScale(this->NormalScale.value()); + } + + if (this->TextureMatCap.has_value()) + { + auto matCapTex = ::GetTexture(this->TextureMatCap.value()); + actor->GetProperty()->SetTexture("matcap", matCapTex); + originalActor->GetProperty()->SetTexture("matcap", matCapTex); } } - for (auto& psActorAndMapper : this->Importer->GetPointSpritesActorsAndMappers()) + for ([[maybe_unused]] const auto& [actor, mapper] : this->Importer->GetPointSpritesActorsAndMappers()) { - psActorAndMapper.first->GetProperty()->SetColor(this->SurfaceColor); - psActorAndMapper.first->GetProperty()->SetOpacity(this->Opacity); + if(surfaceColor) + { + actor->GetProperty()->SetColor(surfaceColor); + } + + if (this->Opacity.has_value()) + { + actor->GetProperty()->SetOpacity(this->Opacity.value()); + } } this->ColoringActorsPropertiesConfigured = true; @@ -252,10 +345,7 @@ void vtkF3DRendererWithColoring::ConfigureColoringActorsProperties() //---------------------------------------------------------------------------- void vtkF3DRendererWithColoring::SetPointSpritesProperties(SplatType type, double pointSpritesSize) { - if (!this->Importer) - { - return; - } + assert(this->Importer); if (type == SplatType::GAUSSIAN) { @@ -277,11 +367,10 @@ void vtkF3DRendererWithColoring::SetPointSpritesProperties(SplatType type, doubl scaleFactor = pointSpritesSize * bbox.GetDiagonalLength() * 0.001; } - const auto& psActorsAndMappers = this->Importer->GetPointSpritesActorsAndMappers(); - for (auto& [actor, mapper] : psActorsAndMappers) + for (const auto& [actor, mapper] : this->Importer->GetPointSpritesActorsAndMappers()) { - mapper->EmissiveOff(); + mapper->EmissiveOff(); if (type == SplatType::GAUSSIAN) { mapper->SetScaleFactor(1.0); @@ -365,21 +454,16 @@ void vtkF3DRendererWithColoring::SetUseVolume(bool use) //---------------------------------------------------------------------------- void vtkF3DRendererWithColoring::SetUseInverseOpacityFunction(bool use) { - if (!this->Importer) - { - return; - } + assert(this->Importer); if (this->UseInverseOpacityFunction != use) { this->UseInverseOpacityFunction = use; - - const auto& volPropsAndMappers = this->Importer->GetVolumePropsAndMappers(); - for (auto& volPropAndMapper : volPropsAndMappers) + for ([[maybe_unused]] const auto& [prop, mapper] : this->Importer->GetVolumePropsAndMappers()) { - if (volPropAndMapper.first) + if (prop) { - vtkPiecewiseFunction* pwf = volPropAndMapper.first->GetProperty()->GetScalarOpacity(); + vtkPiecewiseFunction* pwf = prop->GetProperty()->GetScalarOpacity(); if (pwf->GetSize() == 2) { double range[2]; @@ -404,7 +488,7 @@ void vtkF3DRendererWithColoring::SetScalarBarRange(const std::optionalUserScalarBarRange = range; this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; + this->ColoringMappersConfigured = false; this->PointSpritesMappersConfigured = false; this->VolumePropsAndMappersConfigured = false; this->ScalarBarActorConfigured = false; @@ -420,9 +504,10 @@ void vtkF3DRendererWithColoring::SetColormap(const std::vector& colormap this->Colormap = colormap; this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; + this->ColoringMappersConfigured = false; this->PointSpritesMappersConfigured = false; this->VolumePropsAndMappersConfigured = false; + this->ScalarBarActorConfigured = false; this->ColoringConfigured = false; } @@ -431,11 +516,6 @@ void vtkF3DRendererWithColoring::SetColormap(const std::vector& colormap //---------------------------------------------------------------------------- void vtkF3DRendererWithColoring::CycleScalars(CycleType type) { - if (!this->Importer) - { - return; - } - switch (type) { case (CycleType::NONE): @@ -458,7 +538,7 @@ void vtkF3DRendererWithColoring::CycleScalars(CycleType type) this->CycleScalars(this->CheckColoring()); this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; + this->ColoringMappersConfigured = false; this->PointSpritesMappersConfigured = false; this->VolumePropsAndMappersConfigured = false; this->ScalarBarActorConfigured = false; @@ -484,7 +564,7 @@ vtkF3DRendererWithColoring::CycleType vtkF3DRendererWithColoring::CheckColoring( } // Suggest to change the array index only if current index is not valid - vtkF3DGenericImporter::ColoringInfo info; + vtkF3DMetaImporter::ColoringInfo info; if (!this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info)) { return CycleType::ARRAY_INDEX; @@ -503,10 +583,7 @@ vtkF3DRendererWithColoring::CycleType vtkF3DRendererWithColoring::CheckColoring( void vtkF3DRendererWithColoring::SetColoring(bool enable, bool useCellData, const std::optional& arrayName, int component) { - if (!this->Importer) - { - return; - } + assert(this->Importer); // XXX This should be reworked to avoid handling multiple information in one parameters // while still being future-proof and flexible enough. @@ -547,7 +624,7 @@ void vtkF3DRendererWithColoring::SetColoring(bool enable, } this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; + this->ColoringMappersConfigured = false; this->PointSpritesMappersConfigured = false; this->VolumePropsAndMappersConfigured = false; this->ScalarBarActorConfigured = false; @@ -570,8 +647,10 @@ bool vtkF3DRendererWithColoring::GetColoringUseCell() //---------------------------------------------------------------------------- std::optional vtkF3DRendererWithColoring::GetColoringArrayName() { + assert(this->Importer); + std::optional arrayName; - vtkF3DGenericImporter::ColoringInfo info; + vtkF3DMetaImporter::ColoringInfo info; if (this->Importer && this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info)) { arrayName = info.Name; @@ -588,20 +667,17 @@ int vtkF3DRendererWithColoring::GetColoringComponent() //---------------------------------------------------------------------------- void vtkF3DRendererWithColoring::UpdateActors() { - if (!this->Importer) - { - // Importer is nullptr, still call superclass to render other actors - this->Superclass::UpdateActors(); - return; - } + assert(this->Importer); // Handle importer changes + // XXX: Importer only modify itself when adding a new importer, + // not when updating at a time step vtkMTimeType importerMTime = this->Importer->GetMTime(); - bool importerChanged = this->Importer->GetMTime() >= this->ImporterTimeStamp; + bool importerChanged = this->Importer->GetMTime() > this->ImporterTimeStamp; if (importerChanged) { this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; + this->ColoringMappersConfigured = false; this->PointSpritesMappersConfigured = false; this->VolumePropsAndMappersConfigured = false; this->ScalarBarActorConfigured = false; @@ -634,7 +710,7 @@ void vtkF3DRendererWithColoring::ConfigureColoring() assert(this->Importer); // Recover coloring information - vtkF3DGenericImporter::ColoringInfo info; + vtkF3DMetaImporter::ColoringInfo info; bool hasColoring = this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info); @@ -655,61 +731,53 @@ void vtkF3DRendererWithColoring::ConfigureColoring() // Handle surface geometry bool geometriesVisible = this->UseRaytracing || (!this->UseVolume && !this->UsePointSprites); - const auto& actorsAndMappers = this->Importer->GetGeometryActorsAndMappers(); - for (size_t i = 0; i < actorsAndMappers.size(); i++) + for (const auto& [actor, mapper, originalActor] : this->Importer->GetColoringActorsAndMappers()) { - auto& actorAndMapper = actorsAndMappers[i]; - vtkDataArray* coloringArray = nullptr; - if (hasColoring && info.Arrays.size() > i) - { - coloringArray = info.Arrays[i]; - } - actorAndMapper.first->SetVisibility(geometriesVisible); - if (geometriesVisible && coloringArray) + if (geometriesVisible) { - if (!this->GeometryMappersConfigured) + bool visible = false; + if (hasColoring) { - vtkF3DRendererWithColoring::ConfigureMapperForColoring(actorAndMapper.second, coloringArray, - this->ComponentForColoring, this->ColorTransferFunction, this->ColorRange, - this->UseCellColoring); + // Rely on the previous state of scalar visibility to know if we should show the actor by default + visible = mapper->GetScalarVisibility(); + if (!this->ColoringMappersConfigured) + { + visible = vtkF3DRendererWithColoring::ConfigureMapperForColoring(mapper, info.Name, + this->ComponentForColoring, this->ColorTransferFunction, this->ColorRange, + this->UseCellColoring); + } } - actorAndMapper.second->ScalarVisibilityOn(); + actor->SetVisibility(visible); + originalActor->SetVisibility(!visible); } else { - actorAndMapper.second->ScalarVisibilityOff(); + actor->SetVisibility(false); + originalActor->SetVisibility(false); } } if (geometriesVisible) { - this->GeometryMappersConfigured = true; + this->ColoringMappersConfigured = true; } // Handle point sprites bool pointSpritesVisible = !this->UseRaytracing && !this->UseVolume && this->UsePointSprites; - const auto& psActorsAndMappers = this->Importer->GetPointSpritesActorsAndMappers(); - for (size_t i = 0; i < psActorsAndMappers.size(); i++) + for (const auto& [actor, mapper] : this->Importer->GetPointSpritesActorsAndMappers()) { - auto& actorAndMapper = psActorsAndMappers[i]; - vtkDataArray* coloringArray = nullptr; - if (hasColoring && info.Arrays.size() > i) + actor->SetVisibility(pointSpritesVisible); + if (pointSpritesVisible) { - coloringArray = info.Arrays[i]; - } - actorAndMapper.first->SetVisibility(pointSpritesVisible); - if (pointSpritesVisible && coloringArray) - { - if (!this->PointSpritesMappersConfigured) + if (hasColoring) { - vtkF3DRendererWithColoring::ConfigureMapperForColoring(actorAndMapper.second, coloringArray, - this->ComponentForColoring, this->ColorTransferFunction, this->ColorRange, - this->UseCellColoring); + if (!this->PointSpritesMappersConfigured) + { + vtkF3DRendererWithColoring::ConfigureMapperForColoring(mapper, info.Name, + this->ComponentForColoring, this->ColorTransferFunction, this->ColorRange, + this->UseCellColoring); + } } - actorAndMapper.second->ScalarVisibilityOn(); - } - else - { - actorAndMapper.second->ScalarVisibilityOff(); + mapper->SetScalarVisibility(hasColoring); } } if (pointSpritesVisible) @@ -719,38 +787,42 @@ void vtkF3DRendererWithColoring::ConfigureColoring() // Handle Volume prop const auto& volPropsAndMappers = this->Importer->GetVolumePropsAndMappers(); - for (size_t i = 0; i < volPropsAndMappers.size(); i++) + for (const auto& [prop, mapper] : volPropsAndMappers) { - auto& propAndMapper = volPropsAndMappers[i]; - vtkDataArray* coloringArray = nullptr; - if (hasColoring && info.Arrays.size() > i) - { - coloringArray = info.Arrays[i]; - } if (!volumeVisible) { - propAndMapper.first->VisibilityOff(); - } - else if (!coloringArray) - { - F3DLog::Print( - F3DLog::Severity::Error, "Cannot use volume with this dataset or with the requested array"); - propAndMapper.first->VisibilityOff(); + prop->VisibilityOff(); } else { - if (!this->VolumePropsAndMappersConfigured) + bool visible = false; + if (hasColoring) { - vtkF3DRendererWithColoring::ConfigureVolumeForColoring(propAndMapper.second, - propAndMapper.first, coloringArray, this->ComponentForColoring, - this->ColorTransferFunction, this->ColorRange, this->UseCellColoring, - this->UseInverseOpacityFunction); + // Initialize the visibility based on the mapper configuration + visible = !std::string(mapper->GetArrayName()).empty(); + if (!this->VolumePropsAndMappersConfigured) + { + visible = vtkF3DRendererWithColoring::ConfigureVolumeForColoring(mapper, + prop, info.Name, this->ComponentForColoring, + this->ColorTransferFunction, this->ColorRange, this->UseCellColoring, + this->UseInverseOpacityFunction); + if (!visible) + { + F3DLog::Print( + F3DLog::Severity::Warning, "Cannot find the array \"" + info.Name + "\" to display volume with"); + } + } } - propAndMapper.first->VisibilityOn(); + prop->SetVisibility(visible); } } if (volumeVisible) { + if (!this->VolumePropsAndMappersConfigured && volPropsAndMappers.size() == 0) + { + F3DLog::Print( + F3DLog::Severity::Error, "Cannot use volume with this data"); + } this->VolumePropsAndMappersConfigured = true; } @@ -771,13 +843,10 @@ void vtkF3DRendererWithColoring::ConfigureColoring() //---------------------------------------------------------------------------- std::string vtkF3DRendererWithColoring::GetColoringDescription() { - if (!this->Importer) - { - return ""; - } + assert(this->Importer); std::stringstream stream; - vtkF3DGenericImporter::ColoringInfo info; + vtkF3DMetaImporter::ColoringInfo info; if (this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info)) { stream << "Coloring using " << (this->UseCellColoring ? "cell" : "point") << " array named " @@ -792,16 +861,21 @@ std::string vtkF3DRendererWithColoring::GetColoringDescription() } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ConfigureMapperForColoring(vtkPolyDataMapper* mapper, - vtkDataArray* array, int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag) +bool vtkF3DRendererWithColoring::ConfigureMapperForColoring(vtkPolyDataMapper* mapper, const std::string& name, + int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag) { + vtkDataSetAttributes* data = cellFlag ? + static_cast(mapper->GetInput()->GetCellData()) : + static_cast(mapper->GetInput()->GetPointData()); + vtkDataArray* array = data->GetArray(name.c_str()); if (!array || component >= array->GetNumberOfComponents()) { - return; + mapper->ScalarVisibilityOff(); + return false; } mapper->SetColorModeToMapScalars(); - mapper->SelectColorArray(array->GetName()); + mapper->SelectColorArray(name.c_str()); mapper->SetScalarMode( cellFlag ? VTK_SCALAR_MODE_USE_CELL_FIELD_DATA : VTK_SCALAR_MODE_USE_POINT_FIELD_DATA); mapper->ScalarVisibilityOn(); @@ -813,6 +887,7 @@ void vtkF3DRendererWithColoring::ConfigureMapperForColoring(vtkPolyDataMapper* m // comp > 4 is actually not supported and would fail with a vtk error F3DLog::Print(F3DLog::Severity::Warning, "Direct scalars rendering not supported by array with more than 4 components"); + return false; } else { @@ -825,21 +900,28 @@ void vtkF3DRendererWithColoring::ConfigureMapperForColoring(vtkPolyDataMapper* m mapper->SetScalarRange(range); mapper->SetLookupTable(ctf); } + return true; } //---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ConfigureVolumeForColoring(vtkSmartVolumeMapper* mapper, - vtkVolume* volume, vtkDataArray* array, int component, vtkColorTransferFunction* ctf, +bool vtkF3DRendererWithColoring::ConfigureVolumeForColoring(vtkSmartVolumeMapper* mapper, + vtkVolume* volume, const std::string& name, int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag, bool inverseOpacityFlag) { + vtkDataSetAttributes* data = cellFlag ? + static_cast(mapper->GetInput()->GetCellData()) : + static_cast(mapper->GetInput()->GetPointData()); + vtkDataArray* array = data->GetArray(name.c_str()); if (!array || component >= array->GetNumberOfComponents()) { - return; + // We rely on the selected scalar array to check if this mapper can be shown or not + mapper->SelectScalarArray(""); + return false; } mapper->SetScalarMode( cellFlag ? VTK_SCALAR_MODE_USE_CELL_FIELD_DATA : VTK_SCALAR_MODE_USE_POINT_FIELD_DATA); - mapper->SelectScalarArray(array->GetName()); + mapper->SelectScalarArray(name.c_str()); if (component >= 0) { @@ -857,6 +939,7 @@ void vtkF3DRendererWithColoring::ConfigureVolumeForColoring(vtkSmartVolumeMapper // comp > 4 is actually not supported and would fail with a vtk error F3DLog::Print(F3DLog::Severity::Warning, "Direct scalars rendering not supported by array with more than 4 components"); + return false; } else { @@ -875,6 +958,7 @@ void vtkF3DRendererWithColoring::ConfigureVolumeForColoring(vtkSmartVolumeMapper property->SetInterpolationTypeToLinear(); volume->SetProperty(property); + return true; } //---------------------------------------------------------------------------- @@ -896,7 +980,7 @@ void vtkF3DRendererWithColoring::ConfigureScalarBarActorForColoring( //---------------------------------------------------------------------------- void vtkF3DRendererWithColoring::ConfigureRangeAndCTFForColoring( - const vtkF3DGenericImporter::ColoringInfo& info) + const vtkF3DMetaImporter::ColoringInfo& info) { if (this->ComponentForColoring == -2) { @@ -978,13 +1062,9 @@ void vtkF3DRendererWithColoring::ConfigureRangeAndCTFForColoring( //---------------------------------------------------------------------------- void vtkF3DRendererWithColoring::FillCheatSheetHotkeys(std::stringstream& cheatSheetText) { - if (!this->Importer) - { - this->Superclass::FillCheatSheetHotkeys(cheatSheetText); - return; - } + assert(this->Importer); - vtkF3DGenericImporter::ColoringInfo info; + vtkF3DMetaImporter::ColoringInfo info; bool hasColoring = this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info); @@ -1039,7 +1119,7 @@ void vtkF3DRendererWithColoring::CycleComponentForColoring() { assert(this->Importer); - vtkF3DGenericImporter::ColoringInfo info; + vtkF3DMetaImporter::ColoringInfo info; if (!this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info)) { return; @@ -1053,10 +1133,7 @@ void vtkF3DRendererWithColoring::CycleComponentForColoring() //---------------------------------------------------------------------------- std::string vtkF3DRendererWithColoring::GenerateMetaDataDescription() { - if (!this->Importer) - { - return " Unavailable\n"; - } + assert(this->Importer); // XXX Padding should not be handled by manipulating string // but on the actor directly, but it is not supported by VTK yet. @@ -1095,7 +1172,7 @@ std::string vtkF3DRendererWithColoring::ComponentToString(int component) } else { - vtkF3DGenericImporter::ColoringInfo info; + vtkF3DMetaImporter::ColoringInfo info; if (!this->Importer->GetInfoForColoring( this->UseCellColoring, this->ArrayIndexForColoring, info)) { diff --git a/vtkext/private/module/vtkF3DRendererWithColoring.h b/vtkext/private/module/vtkF3DRendererWithColoring.h index 7558527d26..667793a341 100644 --- a/vtkext/private/module/vtkF3DRendererWithColoring.h +++ b/vtkext/private/module/vtkF3DRendererWithColoring.h @@ -12,7 +12,7 @@ #include "vtkF3DRenderer.h" -#include "vtkF3DGenericImporter.h" +#include "vtkF3DMetaImporter.h" #include @@ -20,6 +20,7 @@ class vtkColorTransferFunction; class vtkDataArray; class vtkDataSetAttributes; class vtkScalarBarActor; +class vtkSmartVolumeMapper; class vtkF3DRendererWithColoring : public vtkF3DRenderer { @@ -30,64 +31,64 @@ class vtkF3DRendererWithColoring : public vtkF3DRenderer /** * Initialize all actors and flags */ - void Initialize(const std::string& up) override; + void Initialize() override; /** - * Set the roughness on all coloring actors + * Set the roughness on all actors */ - void SetRoughness(double roughness); + void SetRoughness(const std::optional& roughness); /** - * Set the surface color on all coloring actors + * Set the surface color on all actors */ - void SetSurfaceColor(double* color); + void SetSurfaceColor(const std::optional>& color); /** - * Set the emmissive factors on all coloring actors + * Set the emmissive factors on all actors */ - void SetEmissiveFactor(double* factors); + void SetEmissiveFactor(const std::optional>& factors); /** - * Set the opacity on all coloring actors + * Set the opacity on all actors */ - void SetOpacity(double opacity); + void SetOpacity(const std::optional& opacity); /** - * Set the metallic on all coloring actors + * Set the metallic on all actors */ - void SetMetallic(double metallic); + void SetMetallic(const std::optional& metallic); /** - * Set the normal scale on all coloring actors + * Set the normal scale on all actors */ - void SetNormalScale(double normalScale); + void SetNormalScale(const std::optional& normalScale); /** - * Set the material capture texture on all coloring actors. + * Set the material capture texture on all actors. * This texture includes baked lighting effect, * so all other material textures are ignored. */ - void SetTextureMatCap(const std::string& tex); + void SetTextureMatCap(const std::optional& tex); /** - * Set the base color texture on all coloring actors + * Set the base color texture on all actors */ - void SetTextureBaseColor(const std::string& tex); + void SetTextureBaseColor(const std::optional& tex); /** - * Set the metarial texture on all coloring actors + * Set the material texture on all actors */ - void SetTextureMaterial(const std::string& tex); + void SetTextureMaterial(const std::optional& tex); /** - * Set the emissive texture on all coloring actors + * Set the emissive texture on all actors */ - void SetTextureEmissive(const std::string& tex); + void SetTextureEmissive(const std::optional& tex); /** - * Set the normal texture on all coloring actors + * Set the normal texture on all actors */ - void SetTextureNormal(const std::string& tex); + void SetTextureNormal(const std::optional& tex); enum class SplatType { @@ -102,8 +103,8 @@ class vtkF3DRendererWithColoring : public vtkF3DRenderer /** * Set the visibility of the scalar bar. - * It will only be shown when coloring and not - * using direct scalars rendering. + * It will only be shown when coloring and not shown + * when using direct scalars rendering. */ void ShowScalarBar(bool show); @@ -152,9 +153,9 @@ class vtkF3DRendererWithColoring : public vtkF3DRenderer void CycleScalars(CycleType type); /** - * Set the generic importer to handle coloring and other actors related actions with. + * Set the meta importer to recover coloring information from */ - void SetImporter(vtkF3DGenericImporter* importer); + void SetImporter(vtkF3DMetaImporter* importer); /** * Set coloring information. @@ -175,8 +176,7 @@ class vtkF3DRendererWithColoring : public vtkF3DRenderer ///@} /** - * Reimplemented to update the visibility and coloring of internal actors - * as well as the scalar bar actors. + * Set properties on each individual actors and also configure the coloring * Call Superclass::UpdateActors at the end. */ void UpdateActors() override; @@ -191,28 +191,29 @@ class vtkF3DRendererWithColoring : public vtkF3DRenderer ~vtkF3DRendererWithColoring() override = default; /** - * Configure all coloring actors properties: - * - roughness - * + * XXX: This method name is semantically incorrect and will soon be changed + * Configure all coloring actors properties */ void ConfigureColoringActorsProperties(); /** - * Configure coloring for all coloring actors + * Configure coloring for all actors */ void ConfigureColoring(); /** * Convenience method for configuring a poly data mapper for coloring + * Return true if mapper was configured for coloring, false otherwise. */ - static void ConfigureMapperForColoring(vtkPolyDataMapper* mapper, vtkDataArray* array, + static bool ConfigureMapperForColoring(vtkPolyDataMapper* mapper, const std::string& name, int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag = false); /** - * Convenience method for configuring a volume mapper and volume for coloring + * Convenience method for configuring a volume mapper and volume prop for coloring + * Return true if they were configured for coloring, false otherwise. */ - static void ConfigureVolumeForColoring(vtkSmartVolumeMapper* mapper, vtkVolume* volume, - vtkDataArray* array, int component, vtkColorTransferFunction* ctf, double range[2], + static bool ConfigureVolumeForColoring(vtkSmartVolumeMapper* mapper, vtkVolume* volume, + const std::string& name, int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag = false, bool inverseOpacityFlag = false); /** @@ -225,7 +226,7 @@ class vtkF3DRendererWithColoring : public vtkF3DRenderer * Configure internal range and color transfer function according to provided * coloring info */ - void ConfigureRangeAndCTFForColoring(const vtkF3DGenericImporter::ColoringInfo& info); + void ConfigureRangeAndCTFForColoring(const vtkF3DMetaImporter::ColoringInfo& info); /** * Fill cheatsheet hotkeys string stream @@ -268,28 +269,29 @@ class vtkF3DRendererWithColoring : public vtkF3DRenderer */ std::string ComponentToString(int component); - vtkWeakPointer Importer = nullptr; + vtkF3DMetaImporter* Importer = nullptr; + vtkMTimeType ImporterTimeStamp = 0; vtkNew ScalarBarActor; bool ScalarBarActorConfigured = false; - bool GeometryMappersConfigured = false; + bool ColoringMappersConfigured = false; bool PointSpritesMappersConfigured = false; bool VolumePropsAndMappersConfigured = false; bool ColoringActorsPropertiesConfigured = false; bool ColoringConfigured = false; - double Opacity = 1.; - double Roughness = 0.3; - double Metallic = 0.; - double NormalScale = 1.; - double SurfaceColor[3] = { 1., 1., 1. }; - double EmissiveFactor[3] = { 1., 1., 1. }; - std::string TextureMatCap; - std::string TextureBaseColor; - std::string TextureMaterial; - std::string TextureEmissive; - std::string TextureNormal; + std::optional Opacity; + std::optional Roughness; + std::optional Metallic; + std::optional NormalScale; + std::optional> SurfaceColor; + std::optional> EmissiveFactor; + std::optional TextureMatCap; + std::optional TextureBaseColor; + std::optional TextureMaterial; + std::optional TextureEmissive; + std::optional TextureNormal; vtkSmartPointer ColorTransferFunction; double ColorRange[2] = { 0.0, 1.0 }; @@ -306,8 +308,6 @@ class vtkF3DRendererWithColoring : public vtkF3DRenderer std::optional> UserScalarBarRange; std::vector Colormap; - - vtkMTimeType ImporterTimeStamp = 0; }; #endif diff --git a/webassembly/F3DEmscriptenBindings.cxx b/webassembly/F3DEmscriptenBindings.cxx index abf0d77a58..a179dac01d 100644 --- a/webassembly/F3DEmscriptenBindings.cxx +++ b/webassembly/F3DEmscriptenBindings.cxx @@ -57,21 +57,17 @@ f3d::loader* getLoaderPtr(f3d::engine& e) { return &e.getLoader(); } -f3d::loader* loadGeometry(f3d::loader& l, const std::string& p) +f3d::loader* add(f3d::loader& l, const std::string& p) { - return &l.loadGeometry(p, true); + return &l.add(p); } -f3d::loader* loadScene(f3d::loader& l, const std::string& p) +bool supports(f3d::loader& l, const std::string& p) { - return &l.loadScene(p); + return l.supports(p); } -bool hasGeometryReader(f3d::loader& l, const std::string& p) +f3d::loader* clear(f3d::loader& l) { - return l.hasGeometryReader(p); -} -bool hasSceneReader(f3d::loader& l, const std::string& p) -{ - return l.hasSceneReader(p); + return &l.clear(); } f3d::window* getWindowPtr(f3d::engine& e) @@ -104,10 +100,9 @@ EMSCRIPTEN_BINDINGS(f3d) // f3d::loader emscripten::class_("Loader") - .function("loadGeometry", &loadGeometry, emscripten::allow_raw_pointers()) - .function("loadScene", &loadScene, emscripten::allow_raw_pointers()) - .function("hasGeometryReader", &hasGeometryReader, emscripten::allow_raw_pointers()) - .function("hasSceneReader", &hasSceneReader, emscripten::allow_raw_pointers()); + .function("supported", &supported, emscripten::allow_raw_pointers()) + .function("add", &add, emscripten::allow_raw_pointers()) + .function("clear", &clear, emscripten::allow_raw_pointers()); // f3d::window emscripten::class_("Window") diff --git a/webassembly/app.html b/webassembly/app.html index 7deb4e5d68..2ca5d6f485 100644 --- a/webassembly/app.html +++ b/webassembly/app.html @@ -125,13 +125,9 @@

F3D Web

document.getElementById('file-name').innerHTML = name; const filePath = '/' + name; const loader = Module.engineInstance.getLoader(); - if (loader.hasSceneReader(filePath)) + if (loader.supports(filePath)) { - loader.loadScene(filePath); - } - else if (loader.hasGeometryReader(filePath)) - { - loader.loadGeometry(filePath); + loader.add(filePath); } else {