Skip to content
1 change: 1 addition & 0 deletions application/F3DOptionsTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ static inline const std::map<std::string_view, std::string_view> LibOptionsNames
{ "animation-indices", "scene.animation.indices" },
{ "animation-speed-factor", "scene.animation.speed_factor" },
{ "force-reader", "scene.force_reader" },
{ "skip-content-check", "scene.skip_content_check" },
Comment thread
mwestphal marked this conversation as resolved.
{ "font-file", "ui.font_file" },
{ "font-scale", "ui.scale" },
{ "font-color", "ui.font_color" },
Expand Down
27 changes: 22 additions & 5 deletions application/F3DStarter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1667,11 +1667,12 @@ void F3DStarter::LoadFileGroupInternal(

try
{
f3d::reader_types::file_availability availability = scene.supports(tmpPath);
if (!fs::exists(tmpPath))
{
f3d::log::error(tmpPath.string(), " does not exist");
}
else if (scene.supports(tmpPath))
else if (availability == f3d::reader_types::file_availability::AVAILABLE)
{
// Check the size of the file before loading it
static constexpr int BYTES_IN_MIB = 1048576;
Expand All @@ -1687,7 +1688,22 @@ void F3DStarter::LoadFileGroupInternal(
localPaths.emplace_back(tmpPath);
}
}
else
else if (availability == f3d::reader_types::file_availability::UNSUPPORTED_EXSTENSION)
{
auto forceReader = this->Internals->LibOptions.scene.force_reader;
if (forceReader)
{
f3d::log::warn("Forced reader ", *forceReader, " doesn't exist");
}
else
{
f3d::log::warn(tmpPath.string(),
" is of an unknown format, use "
"--force-reader to select a specific reader");
}
unsupported = true;
}
else if (availability == f3d::reader_types::file_availability::UNSUPPORTED_CONTENT)
{
auto forceReader = this->Internals->LibOptions.scene.force_reader;
if (forceReader)
Expand All @@ -1697,7 +1713,7 @@ void F3DStarter::LoadFileGroupInternal(
else
{
f3d::log::warn(tmpPath.string(),
" is of an unknown format or contains unsupported contents, use "
" contains unsupported contents, use "
"--force-reader to select a specific reader");
}
unsupported = true;
Expand Down Expand Up @@ -1822,7 +1838,7 @@ void F3DStarter::LoadFileGroupInternal(

// Unwatch and erase paths that should not be watched anymore
for (auto it = this->Internals->FolderWatchIds.begin();
it != this->Internals->FolderWatchIds.end();)
it != this->Internals->FolderWatchIds.end();)
{
const fs::path& path = it->first;
const dmon_watch_id& dmonId = it->second;
Expand Down Expand Up @@ -2307,7 +2323,8 @@ void F3DStarter::AddCommands()

interactor.addCommand(
"load_next_file_group",
[this](const std::vector<std::string>& args) {
[this](const std::vector<std::string>& args)
{
this->LoadRelativeFileGroup(
+1, parse_optional_bool_flag(args, "load_next_file_group", false));
},
Expand Down
3 changes: 2 additions & 1 deletion c/scene_c_api.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ int f3d_scene_supports(f3d_scene_t* scene, const char* file_path)
}

f3d::scene* cpp_scene = reinterpret_cast<f3d::scene*>(scene);
return cpp_scene->supports(std::filesystem::path(file_path)) ? 1 : 0;
auto availability = cpp_scene->supports(std::filesystem::path(file_path));
return static_cast<int>(availability);
}

//----------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions doc/libf3d/03-OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ Force a specific reader to be used, disregarding the file extension and file con

CLI: `--force-reader`.

### `--skip-content-check` (_bool_, default: `false`)
Comment thread
Prospect138 marked this conversation as resolved.
Outdated

Make attempt to read file without checking it's header.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Make attempt to read file without checking it's header.
Select reader to read file without checking its header content but only based on the file extension.


CLI: `--skip-content-check`.

### `scene.camera.orthographic` (_bool_, optional)

Set to true to force orthographic projection. Model-specified by default, which is false if not specified.
Expand Down
4 changes: 4 additions & 0 deletions doc/user/03-OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ List available _readers_ and exit. Ignore `--verbose`.

Force a specific [reader](02-SUPPORTED_FORMATS.md) to be used, disregarding the file extension and file content.

### `--skip-content-check` (_bool_, default: `false`)

Make attempt to read file without checking it's header.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Make attempt to read file without checking it's header.
Select reader to read file without checking its header content, but only based on the file extension.


### `--list-bindings`

List available _bindings_ and exit. Ignore `--verbose`.
Expand Down
4 changes: 2 additions & 2 deletions java/F3DSceneBindings.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,9 @@ extern "C"
}

const char* str = env->GetStringUTFChars(filePath, nullptr);
bool result = GetEngine(env, self)->getScene().supports(str);
f3d::reader_types::file_availability result = GetEngine(env, self)->getScene().supports(str);
env->ReleaseStringUTFChars(filePath, str);
return result;
return result != f3d::reader_types::file_availability::AVAILABLE;
}

JNIEXPORT jobject JAVA_BIND(Scene, loadAnimationTime)(
Expand Down
6 changes: 5 additions & 1 deletion library/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
},
"force_reader": {
"type": "string"
},
"skip_content_check": {
"type": "bool"
}
},
"render": {
Expand Down Expand Up @@ -396,7 +399,8 @@
},
"colormap": {
"type": "colormap",
"default_value": "0.0, 0.0, 0.0, 0.0, 0.4, 0.9, 0.0, 0.0, 0.8, 0.9, 0.9, 0.0, 1.0, 1.0, 1.0, 1.0"
"default_value":
"0.0, 0.0, 0.0, 0.0, 0.4, 0.9, 0.0, 0.0, 0.8, 0.9, 0.9, 0.0, 1.0, 1.0, 1.0, 1.0"
},
"opacity_map": {
"type": "double_vector",
Expand Down
36 changes: 25 additions & 11 deletions library/plugin/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
#include <vtkImporter.h>
#include <vtkSmartPointer.h>

#include "../public/reader_types.h"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

#include <algorithm>
#include <cctype>
#include <map>
#include <optional>
#include <string>
#include <vector>

Expand Down Expand Up @@ -66,26 +69,37 @@ class reader
/**
* Check if this reader can read the given filename - according to its extension and file content
*/
virtual bool canRead(const std::string& fileName) const
virtual bool canRead(const std::string& fileName, const std::optional<bool> skipContentCheck,
reader_types::file_availability& availability) const
{
std::string ext = fileName.substr(fileName.find_last_of(".") + 1);
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);

const std::vector<std::string>& extensions = this->getExtensions();

if (!std::any_of(
if (std::any_of(
extensions.begin(), extensions.end(), [&](const std::string& s) { return s == ext; }))
{
return false;
}

vtkNew<vtkFileResourceStream> stream;
if (!stream->Open(fileName.c_str()))
{
return false;
vtkNew<vtkFileResourceStream> stream;
if (skipContentCheck.has_value() && skipContentCheck.value() == true)
{
availability = reader_types::file_availability::AVAILABLE;
return true;
}
else if (stream->Open(fileName.c_str()))
{
if (this->canRead(stream))
{
availability = reader_types::file_availability::AVAILABLE;
return true;
}
else
{
availability = reader_types::file_availability::UNSUPPORTED_CONTENT;
}
}
}

return this->canRead(stream);
return false;
}

/**
Expand Down
7 changes: 4 additions & 3 deletions library/private/factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ class factory
/**
* Get the reader that can read the given file, nullptr if none
*/
reader* getReader(const std::string& fileName, std::optional<std::string> forceReader);
reader* getReader(const std::string& fileName, std::optional<std::string> forceReader,
const std::optional<bool> skipContentCheck, reader_types::file_availability& availability);

/**
* Get the reader that can read the given buffer, nullptr if none
*/
reader* getReader(
const std::byte* buffer, std::size_t size, std::optional<std::string> forceReader);
reader* getReader(const std::byte* buffer, std::size_t size,
std::optional<std::string> forceReader, const std::optional<bool> skipContentCheck);

/**
* Get the list of the registered plugins
Expand Down
2 changes: 1 addition & 1 deletion library/private/scene_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class scene_impl : public scene
scene& updateLight(int index, const light_state_t& lightState) override;
scene& removeLight(int index) override;
scene& removeAllLights() override;
bool supports(const std::filesystem::path& filePath) override;
reader_types::file_availability supports(const std::filesystem::path& filePath) override;
scene& loadAnimationTime(double timeValue) override;
std::pair<double, double> animationTimeRange() override;
std::vector<double> getAnimationKeyFrames() override;
Expand Down
21 changes: 21 additions & 0 deletions library/public/reader_types.h
Comment thread
Prospect138 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef f3d_reader_types_h
#define f3d_reader_types_h

#include <cstdint>

namespace f3d
{
namespace reader_types
{

enum class file_availability : std::uint8_t
{
AVAILABLE = 1,
Comment thread
Prospect138 marked this conversation as resolved.
Outdated
UNSUPPORTED_EXSTENSION = 2,
Comment thread
Prospect138 marked this conversation as resolved.
Outdated
UNSUPPORTED_CONTENT = 3,
};

} // namespace reader_types
} // namespace f3d

#endif
4 changes: 3 additions & 1 deletion library/public/scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "exception.h"
#include "export.h"
#include "reader_types.h"
#include "mesh_view.h"
#include "types.h"

Expand Down Expand Up @@ -156,7 +157,8 @@ class F3D_EXPORT scene
* scene.force_reader is taken into account and plugin should be loaded for their readers to be
* found.
*/
[[nodiscard]] virtual bool supports(const std::filesystem::path& filePath) = 0;
[[nodiscard]] virtual reader_types::file_availability supports(
const std::filesystem::path& filePath) = 0;
Comment thread
Prospect138 marked this conversation as resolved.
Outdated

/**
* Load added files at provided time value if they contain any animation
Expand Down
18 changes: 10 additions & 8 deletions library/src/factory.cxx.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ namespace f3d
namespace
{
template<typename F>
reader* pickReader(
const std::vector<plugin*>& plugins, std::optional<std::string> forceReader, F&& isValid)
reader* pickReader(const std::vector<plugin*>& plugins, std::optional<std::string> forceReader,
reader_types::file_availability& availability, F&& isValid)
{
int bestScore = -1;
reader* bestReader = nullptr;
Expand Down Expand Up @@ -76,20 +76,22 @@ factory::plugin_initializer_t factory::getStaticInitializer(const std::string& p
}

//----------------------------------------------------------------------------
reader* factory::getReader(const std::string& fileName, std::optional<std::string> forceReader)
reader* factory::getReader(const std::string& fileName, std::optional<std::string> forceReader,
const std::optional<bool> skipContentCheck, reader_types::file_availability& availability)
{
return f3d::pickReader(
this->Plugins, forceReader, [&](const reader* reader) { return reader->canRead(fileName); });
return f3d::pickReader(this->Plugins, forceReader, availability, [&](const reader* reader)
{ return reader->canRead(fileName, skipContentCheck, availability); });
}

//----------------------------------------------------------------------------
reader* factory::getReader(
const std::byte* buffer, std::size_t size, std::optional<std::string> forceReader)
reader* factory::getReader(const std::byte* buffer, std::size_t size,
std::optional<std::string> forceReader, const std::optional<bool> skipContentCheck)
{
vtkNew<vtkMemoryResourceStream> stream;
stream->SetBuffer(buffer, size);
reader_types::file_availability availability;

return f3d::pickReader(this->Plugins, forceReader,
return f3d::pickReader(this->Plugins, forceReader, availability,
[&](const reader* reader) { return reader->supportsStream() && reader->canRead(stream); });
}

Expand Down
43 changes: 34 additions & 9 deletions library/src/scene_impl.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,12 @@ scene& scene_impl::add(const std::vector<fs::path>& filePaths)
throw scene::load_failure_exception(filePath.string() + " does not exists");
}
std::optional<std::string> forceReader = this->Internals->Options.scene.force_reader;
const std::optional<bool> skipContentCheck = this->Internals->Options.scene.skip_content_check;
// Recover the importer for the provided file path
const f3d::reader* reader = f3d::factory::instance()->getReader(filePath.string(), forceReader);
if (reader)
reader_types::file_availability availability;
const f3d::reader* reader = f3d::factory::instance()->getReader(
filePath.string(), forceReader, skipContentCheck, availability);
if (availability == reader_types::file_availability::AVAILABLE)
{
if (forceReader)
{
Expand All @@ -271,9 +274,25 @@ scene& scene_impl::add(const std::vector<fs::path>& filePaths)
{
throw scene::load_failure_exception(*forceReader + " is not a valid force reader");
}
throw scene::load_failure_exception(filePath.string() +
" is not a file of a supported 3D scene file format, use force reader to force a specific "
"reader");
std::string errorMessage;
switch (availability)
{
case reader_types::file_availability::UNSUPPORTED_EXSTENSION:
errorMessage = (filePath.string() +
" is not a file of a supported 3D scene file format, use force reader to force a "
"specific "
"reader");
Comment on lines +282 to +284

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
" is not a file of a supported 3D scene file format, use force reader to force a "
"specific "
"reader");
" does not have an extension corresponding to a supported file format, use force reader to force a specific reader");

break;
case reader_types::file_availability::UNSUPPORTED_CONTENT:
errorMessage = (filePath.string() +
" contains unsupported content " //! todo add skip content check
"reader");
break;
default:
errorMessage = "Something went wrong";
break;
}
throw scene::load_failure_exception(errorMessage);
}

vtkSmartPointer<vtkImporter> importer = reader->createSceneReader(filePath.string());
Expand Down Expand Up @@ -319,6 +338,7 @@ scene& scene_impl::add(const std::byte* buffer, std::size_t size)

// Recover the appropriate reader
std::optional<std::string> forceReader = this->Internals->Options.scene.force_reader;
const std::optional<bool> skipContentCheck = this->Internals->Options.scene.skip_content_check;

#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 6, 20260128)
if (!forceReader)
Expand All @@ -328,7 +348,8 @@ scene& scene_impl::add(const std::byte* buffer, std::size_t size)
}
#endif

const f3d::reader* reader = f3d::factory::instance()->getReader(buffer, size, forceReader);
const f3d::reader* reader =
f3d::factory::instance()->getReader(buffer, size, forceReader, skipContentCheck);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
f3d::factory::instance()->getReader(buffer, size, forceReader, skipContentCheck);
f3d::factory::instance()->getReader(buffer, size, forceReader, this->Internals->Options.scene.skip_content_check);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but how can this ever work ? How is the reader selected when there is no extension and no content check ?

if (reader)
{
if (forceReader)
Expand Down Expand Up @@ -849,10 +870,14 @@ scene& scene_impl::removeAllLights()
}

//----------------------------------------------------------------------------
bool scene_impl::supports(const fs::path& filePath)
f3d::reader_types::file_availability scene_impl::supports(const fs::path& filePath)
{
return f3d::factory::instance()->getReader(
filePath.string(), this->Internals->Options.scene.force_reader) != nullptr;
f3d::reader_types::file_availability availability =
reader_types::file_availability::UNSUPPORTED_EXSTENSION;
f3d::factory::instance()->getReader(filePath.string(),
this->Internals->Options.scene.force_reader, this->Internals->Options.scene.skip_content_check,
availability) != nullptr;
return availability;
}

//----------------------------------------------------------------------------
Expand Down
Loading