From 9fd7d4396b0f87a453f6a5f25f49de685262db28 Mon Sep 17 00:00:00 2001 From: Michael Migliore Date: Sat, 26 Nov 2022 21:30:15 +0100 Subject: [PATCH] Cache baked HDRI --- .github/actions/vtk_commit_sha | 2 +- cmake/testing.cmake | 4 +- .../VTKExtensions/Rendering/CMakeLists.txt | 2 + .../Rendering/Testing/CMakeLists.txt | 1 + .../Testing/TestF3DCachedTextures.cxx | 16 ++ library/VTKExtensions/Rendering/vtk.module | 1 + .../Rendering/vtkF3DCachedLUTTexture.cxx | 68 +++++++ .../Rendering/vtkF3DCachedLUTTexture.h | 42 ++++ .../Rendering/vtkF3DCachedSpecularTexture.cxx | 87 ++++++++ .../Rendering/vtkF3DCachedSpecularTexture.h | 39 ++++ .../Rendering/vtkF3DRenderer.cxx | 185 +++++++++++++++++- .../VTKExtensions/Rendering/vtkF3DRenderer.h | 8 + library/private/window_impl.h | 6 + library/public/engine.h | 10 + library/src/engine.cxx | 22 +++ library/src/window_impl.cxx | 18 ++ python/F3DPythonBindings.cxx | 1 + testing/baselines/TestHDRI8Bit.png | 4 +- testing/baselines/TestHDRIEdges.png | 4 +- testing/baselines/TestHDRIOrient.png | 4 +- 20 files changed, 511 insertions(+), 13 deletions(-) create mode 100644 library/VTKExtensions/Rendering/Testing/TestF3DCachedTextures.cxx create mode 100644 library/VTKExtensions/Rendering/vtkF3DCachedLUTTexture.cxx create mode 100644 library/VTKExtensions/Rendering/vtkF3DCachedLUTTexture.h create mode 100644 library/VTKExtensions/Rendering/vtkF3DCachedSpecularTexture.cxx create mode 100644 library/VTKExtensions/Rendering/vtkF3DCachedSpecularTexture.h diff --git a/.github/actions/vtk_commit_sha b/.github/actions/vtk_commit_sha index 14aae02a29..626270c60e 100644 --- a/.github/actions/vtk_commit_sha +++ b/.github/actions/vtk_commit_sha @@ -1 +1 @@ -ea2b6b5ad1516f65684eff0972a198af873ca1c1 +7ae47ffa119c6feaea43ef021fb48a3056abbec2 diff --git a/cmake/testing.cmake b/cmake/testing.cmake index 06626bf450..7515f90196 100644 --- a/cmake/testing.cmake +++ b/cmake/testing.cmake @@ -228,8 +228,8 @@ if(VTK_VERSION VERSION_GREATER_EQUAL 9.1.20211007) f3d_test(NAME TestThumbnailConfigFileUp DATA suzanne.stl CONFIG ${CMAKE_SOURCE_DIR}/resources/thumbnail.json DEFAULT_LIGHTS) endif() -# HDRI test needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8825 -if(VTK_VERSION VERSION_GREATER_EQUAL 9.1.20220117) +# HDRI test needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/9767 +if(VTK_VERSION VERSION_GREATER_EQUAL 9.2.20221220) f3d_test(NAME TestHDRI HDRI DATA suzanne.ply ARGS --hdri=${CMAKE_SOURCE_DIR}/testing/data/palermo_park_1k.hdr DEFAULT_LIGHTS) f3d_test(NAME TestHDRIBlur HDRI DATA suzanne.ply ARGS -u --hdri=${CMAKE_SOURCE_DIR}/testing/data/palermo_park_1k.hdr DEFAULT_LIGHTS) f3d_test(NAME TestHDRIBlurRatio HDRI DATA suzanne.ply RESOLUTION 600,100 ARGS -u --hdri=${CMAKE_SOURCE_DIR}/testing/data/palermo_park_1k.hdr DEFAULT_LIGHTS) diff --git a/library/VTKExtensions/Rendering/CMakeLists.txt b/library/VTKExtensions/Rendering/CMakeLists.txt index aff01c841c..e1f88fb311 100644 --- a/library/VTKExtensions/Rendering/CMakeLists.txt +++ b/library/VTKExtensions/Rendering/CMakeLists.txt @@ -1,4 +1,6 @@ set(classes + vtkF3DCachedLUTTexture + vtkF3DCachedSpecularTexture vtkF3DInteractorEventRecorder vtkF3DInteractorStyle vtkF3DNoRenderWindow diff --git a/library/VTKExtensions/Rendering/Testing/CMakeLists.txt b/library/VTKExtensions/Rendering/Testing/CMakeLists.txt index cf8d153a4b..8fc7e822b4 100644 --- a/library/VTKExtensions/Rendering/Testing/CMakeLists.txt +++ b/library/VTKExtensions/Rendering/Testing/CMakeLists.txt @@ -8,5 +8,6 @@ vtk_add_test_cxx(VTKExtensionsRenderingTests tests TestF3DOpenGLGridMapper.cxx TestF3DRenderPass.cxx TestF3DRenderer.cxx + TestF3DCachedTextures.cxx ${CMAKE_SOURCE_DIR}/testing/ ${CMAKE_BINARY_DIR}/Testing/Temporary/) vtk_test_cxx_executable(VTKExtensionsRenderingTests tests) diff --git a/library/VTKExtensions/Rendering/Testing/TestF3DCachedTextures.cxx b/library/VTKExtensions/Rendering/Testing/TestF3DCachedTextures.cxx new file mode 100644 index 0000000000..ac4313c428 --- /dev/null +++ b/library/VTKExtensions/Rendering/Testing/TestF3DCachedTextures.cxx @@ -0,0 +1,16 @@ +#include +#include + +#include "vtkF3DCachedLUTTexture.h" +#include "vtkF3DCachedSpecularTexture.h" + +int TestF3DCachedTextures(int argc, char* argv[]) +{ + vtkNew lut; + vtkNew specular; + + lut->Print(cout); + specular->Print(cout); + + return EXIT_SUCCESS; +} diff --git a/library/VTKExtensions/Rendering/vtk.module b/library/VTKExtensions/Rendering/vtk.module index a0465a7b87..562b1f0c3a 100644 --- a/library/VTKExtensions/Rendering/vtk.module +++ b/library/VTKExtensions/Rendering/vtk.module @@ -10,6 +10,7 @@ DEPENDS VTK::opengl PRIVATE_DEPENDS VTK::IOImage + VTK::IOXML VTK::InteractionWidgets VTK::RenderingCore f3d::VTKExtensionsCore diff --git a/library/VTKExtensions/Rendering/vtkF3DCachedLUTTexture.cxx b/library/VTKExtensions/Rendering/vtkF3DCachedLUTTexture.cxx new file mode 100644 index 0000000000..033e5020a7 --- /dev/null +++ b/library/VTKExtensions/Rendering/vtkF3DCachedLUTTexture.cxx @@ -0,0 +1,68 @@ +#include "vtkF3DCachedLUTTexture.h" + +#include "vtkImageData.h" +#include "vtkObjectFactory.h" +#include "vtkOpenGLRenderWindow.h" +#include "vtkRenderer.h" +#include "vtkTextureObject.h" +#include "vtkXMLImageDataReader.h" + +#include + +vtkStandardNewMacro(vtkF3DCachedLUTTexture); + +//------------------------------------------------------------------------------ +void vtkF3DCachedLUTTexture::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + os << indent << "FileName: " << this->FileName << endl; +} + +//------------------------------------------------------------------------------ +void vtkF3DCachedLUTTexture::Load(vtkRenderer* ren) +{ + if (this->GetMTime() > this->LoadTime.GetMTime()) + { + vtkOpenGLRenderWindow* renWin = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow()); + + if (this->TextureObject == nullptr) + { + this->TextureObject = vtkTextureObject::New(); + } + + this->TextureObject->SetContext(renWin); + this->TextureObject->SetFormat(GL_RG); +#ifdef GL_ES_VERSION_3_0 + this->TextureObject->SetInternalFormat(GL_RG8); + this->TextureObject->SetDataType(GL_UNSIGNED_BYTE); +#else + this->TextureObject->SetInternalFormat(GL_RG16); + this->TextureObject->SetDataType(GL_UNSIGNED_SHORT); +#endif + this->TextureObject->SetWrapS(vtkTextureObject::ClampToEdge); + this->TextureObject->SetWrapT(vtkTextureObject::ClampToEdge); + this->TextureObject->SetMinificationFilter(vtkTextureObject::Linear); + this->TextureObject->SetMagnificationFilter(vtkTextureObject::Linear); + + vtkNew reader; + reader->SetFileName(this->FileName.c_str()); + reader->Update(); + + vtkImageData* img = reader->GetOutput(); + int dims[3]; + img->GetDimensions(dims); + +#ifdef GL_ES_VERSION_3_0 + this->TextureObject->Create2DFromRaw( + dims[0], dims[1], 2, VTK_UNSIGNED_CHAR, img->GetScalarPointer()); +#else + this->TextureObject->Create2DFromRaw( + dims[0], dims[1], 2, VTK_UNSIGNED_SHORT, img->GetScalarPointer()); +#endif + + this->RenderWindow = renWin; + this->LoadTime.Modified(); + } + + this->TextureObject->Activate(); +} diff --git a/library/VTKExtensions/Rendering/vtkF3DCachedLUTTexture.h b/library/VTKExtensions/Rendering/vtkF3DCachedLUTTexture.h new file mode 100644 index 0000000000..8136084592 --- /dev/null +++ b/library/VTKExtensions/Rendering/vtkF3DCachedLUTTexture.h @@ -0,0 +1,42 @@ +/** + * @class vtkF3DCachedLUTTexture + * @brief create a LUT texture from a vti file + */ + +#ifndef vtkF3DCachedLUTTexture_h +#define vtkF3DCachedLUTTexture_h + +#include "vtkPBRLUTTexture.h" + +class vtkF3DCachedLUTTexture : public vtkPBRLUTTexture +{ +public: + static vtkF3DCachedLUTTexture* New(); + vtkTypeMacro(vtkF3DCachedLUTTexture, vtkPBRLUTTexture); + void PrintSelf(ostream& os, vtkIndent indent) override; + + ///@{ + /** + * Set/Get the image file name. + */ + vtkGetMacro(FileName, std::string); + vtkSetMacro(FileName, std::string); + ///@} + + /** + * Implement base class method. + */ + void Load(vtkRenderer*) override; + +protected: + vtkF3DCachedLUTTexture() = default; + ~vtkF3DCachedLUTTexture() override = default; + + std::string FileName; + +private: + vtkF3DCachedLUTTexture(const vtkF3DCachedLUTTexture&) = delete; + void operator=(const vtkF3DCachedLUTTexture&) = delete; +}; + +#endif diff --git a/library/VTKExtensions/Rendering/vtkF3DCachedSpecularTexture.cxx b/library/VTKExtensions/Rendering/vtkF3DCachedSpecularTexture.cxx new file mode 100644 index 0000000000..448cdb2dbc --- /dev/null +++ b/library/VTKExtensions/Rendering/vtkF3DCachedSpecularTexture.cxx @@ -0,0 +1,87 @@ +#include "vtkF3DCachedSpecularTexture.h" + +#include "vtkImageData.h" +#include "vtkMultiBlockDataSet.h" +#include "vtkObjectFactory.h" +#include "vtkOpenGLRenderWindow.h" +#include "vtkRenderer.h" +#include "vtkTextureObject.h" +#include "vtkXMLMultiBlockDataReader.h" + +#include + +vtkStandardNewMacro(vtkF3DCachedSpecularTexture); + +//------------------------------------------------------------------------------ +void vtkF3DCachedSpecularTexture::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + os << indent << "FileName: " << this->FileName << endl; +} + +//------------------------------------------------------------------------------ +void vtkF3DCachedSpecularTexture::Load(vtkRenderer* ren) +{ + if (this->GetMTime() > this->LoadTime.GetMTime()) + { + vtkOpenGLRenderWindow* renWin = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow()); + + if (this->TextureObject == nullptr) + { + this->TextureObject = vtkTextureObject::New(); + } + + this->TextureObject->SetContext(renWin); + this->TextureObject->SetFormat(GL_RGB); + this->TextureObject->SetInternalFormat(GL_RGB32F); + this->TextureObject->SetDataType(GL_FLOAT); + this->TextureObject->SetWrapS(vtkTextureObject::ClampToEdge); + this->TextureObject->SetWrapT(vtkTextureObject::ClampToEdge); + this->TextureObject->SetWrapR(vtkTextureObject::ClampToEdge); + this->TextureObject->SetMinificationFilter(vtkTextureObject::LinearMipmapLinear); + this->TextureObject->SetMagnificationFilter(vtkTextureObject::Linear); + this->TextureObject->SetGenerateMipmap(true); + + this->RenderWindow = renWin; + + vtkNew reader; + reader->SetFileName(this->FileName.c_str()); + reader->Update(); + + vtkMultiBlockDataSet* mb = vtkMultiBlockDataSet::SafeDownCast(reader->GetOutput()); + + unsigned int nbLevels = mb->GetNumberOfBlocks(); + + this->TextureObject->SetMaxLevel(nbLevels - 1); + + vtkImageData* firstImg = vtkImageData::SafeDownCast(mb->GetBlock(0)); + + void* data[6]; + for (int i = 0; i < 6; i++) + { + data[i] = firstImg->GetScalarPointer(0, 0, i); + } + + int* firstDims = firstImg->GetDimensions(); + this->TextureObject->CreateCubeFromRaw(firstDims[0], firstDims[1], 3, VTK_FLOAT, data); + + // the mip levels are manually uploaded because there is no abstraction in VTK + for (unsigned int i = 1; i < nbLevels; i++) + { + vtkImageData* img = vtkImageData::SafeDownCast(mb->GetBlock(i)); + int* dims = img->GetDimensions(); + + for (int j = 0; j < 6; j++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, i, + this->TextureObject->GetInternalFormat(VTK_FLOAT, 3, false), dims[0], dims[1], 0, + this->TextureObject->GetFormat(VTK_FLOAT, 3, false), + this->TextureObject->GetDataType(VTK_FLOAT), img->GetScalarPointer(0, 0, j)); + } + } + + this->LoadTime.Modified(); + } + + this->TextureObject->Activate(); +} diff --git a/library/VTKExtensions/Rendering/vtkF3DCachedSpecularTexture.h b/library/VTKExtensions/Rendering/vtkF3DCachedSpecularTexture.h new file mode 100644 index 0000000000..60901de132 --- /dev/null +++ b/library/VTKExtensions/Rendering/vtkF3DCachedSpecularTexture.h @@ -0,0 +1,39 @@ +/** + * @class vtkF3DCachedSpecularTexture + * @brief create a prefiltered specular texture from a vtm file + */ + +#ifndef vtkF3DCachedSpecularTexture_h +#define vtkF3DCachedSpecularTexture_h + +#include "vtkPBRPrefilterTexture.h" + +class vtkF3DCachedSpecularTexture : public vtkPBRPrefilterTexture +{ +public: + static vtkF3DCachedSpecularTexture* New(); + vtkTypeMacro(vtkF3DCachedSpecularTexture, vtkPBRPrefilterTexture); + void PrintSelf(ostream& os, vtkIndent indent) override; + + /** + * Set the image file name. + */ + vtkSetMacro(FileName, std::string); + + /** + * Implement base class method. + */ + void Load(vtkRenderer*) override; + +protected: + vtkF3DCachedSpecularTexture() = default; + ~vtkF3DCachedSpecularTexture() override = default; + + std::string FileName; + +private: + vtkF3DCachedSpecularTexture(const vtkF3DCachedSpecularTexture&) = delete; + void operator=(const vtkF3DCachedSpecularTexture&) = delete; +}; + +#endif diff --git a/library/VTKExtensions/Rendering/vtkF3DRenderer.cxx b/library/VTKExtensions/Rendering/vtkF3DRenderer.cxx index 0fa1841c87..8bd1890df9 100644 --- a/library/VTKExtensions/Rendering/vtkF3DRenderer.cxx +++ b/library/VTKExtensions/Rendering/vtkF3DRenderer.cxx @@ -1,32 +1,46 @@ #include "vtkF3DRenderer.h" #include "F3DLog.h" +#include "vtkF3DCachedLUTTexture.h" +#include "vtkF3DCachedSpecularTexture.h" #include "vtkF3DConfigure.h" #include "vtkF3DOpenGLGridMapper.h" #include "vtkF3DRenderPass.h" -#include "vtkLight.h" -#include "vtkLightCollection.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 #include +#include +#include +#include +#include +#include +#include #include #if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 2, 20220907) @@ -47,6 +61,59 @@ vtkStandardNewMacro(vtkF3DRenderer); +namespace +{ +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 2, 20221220) +//---------------------------------------------------------------------------- +// Compute the MD5 hash of the scalar field of an image data +std::string ComputeImageHash(vtkImageData* image) +{ + unsigned char digest[16]; + char md5Hash[33]; + md5Hash[32] = '\0'; + + unsigned char* content = reinterpret_cast(image->GetScalarPointer()); + int* dims = image->GetDimensions(); + int nbComp = image->GetNumberOfScalarComponents(); + int scalarSize = image->GetScalarSize(); + int size = nbComp * scalarSize * dims[0] * dims[1] * dims[2]; + + vtksysMD5* md5 = vtksysMD5_New(); + vtksysMD5_Initialize(md5); + vtksysMD5_Append(md5, content, size); + vtksysMD5_Finalize(md5, digest); + vtksysMD5_DigestToHex(digest, md5Hash); + vtksysMD5_Delete(md5); + + return md5Hash; +} + +//---------------------------------------------------------------------------- +// Download texture from the GPU to a vtkImageData +vtkSmartPointer SaveTextureToImage( + vtkTextureObject* tex, unsigned int target, unsigned int level, unsigned int size, int type) +{ + unsigned int dims[2] = { size, size }; + vtkIdType incr[2] = { 0, 0 }; + + unsigned int nbFaces = tex->GetTarget() == GL_TEXTURE_CUBE_MAP ? 6 : 1; + + vtkNew img; + img->SetDimensions(size, size, nbFaces); + img->AllocateScalars(type, tex->GetComponents()); + + for (unsigned int i = 0; i < nbFaces; i++) + { + vtkPixelBufferObject* pbo = tex->Download(target + i, level); + + pbo->Download2D(type, img->GetScalarPointer(0, 0, i), dims, tex->GetComponents(), incr); + } + + return img; +} +#endif +} + //---------------------------------------------------------------------------- vtkF3DRenderer::vtkF3DRenderer() { @@ -354,9 +421,15 @@ void vtkF3DRenderer::ShowGrid(bool show) void vtkF3DRenderer::SetHDRIFile(const std::string& hdriFile) { // Check HDRI is different than current one - if (this->HDRIFile != hdriFile) + std::string collapsedHdriFile; + if (!hdriFile.empty()) + { + collapsedHdriFile = vtksys::SystemTools::CollapseFullPath(hdriFile); + } + + if (this->HDRIFile != collapsedHdriFile) { - this->HDRIFile = hdriFile; + this->HDRIFile = collapsedHdriFile; // Read HDRI when needed vtkNew hdriTexture; @@ -402,6 +475,51 @@ void vtkF3DRenderer::SetHDRIFile(const std::string& hdriFile) // Dynamic HDRI if (this->HasHDRI) { +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 2, 20221220) + // Check LUT cache + std::string lutCachePath = this->CachePath + "/lut.vti"; + bool lutCacheExists = vtksys::SystemTools::FileExists(lutCachePath, true); + if (lutCacheExists) + { + vtkF3DCachedLUTTexture* lut = vtkF3DCachedLUTTexture::New(); + lut->SetFileName(lutCachePath.c_str()); + this->EnvMapLookupTable = lut; + } + + // Compute HDRI MD5 + std::string hash = ::ComputeImageHash(hdriTexture->GetInput()); + + // Cache folder for this HDRI + std::string currentCachePath = this->CachePath + "/" + hash; + + // Create the folder if it does not exists + vtksys::SystemTools::MakeDirectory(currentCachePath); + + // Check spherical harmonics cache + std::string shCachePath = this->CachePath + "/" + hash + "/sh.vtt"; + bool shCacheExists = vtksys::SystemTools::FileExists(shCachePath, true); + if (shCacheExists) + { + vtkNew reader; + reader->SetFileName(shCachePath.c_str()); + reader->Update(); + + this->SphericalHarmonics = vtkFloatArray::SafeDownCast(reader->GetOutput()->GetColumn(0)); + } + + // Check specular cache + std::string specCachePath = this->CachePath + "/" + hash + "/specular.vtm"; + bool specCacheExists = vtksys::SystemTools::FileExists(specCachePath, true); + if (specCacheExists) + { + vtkF3DCachedSpecularTexture* spec = vtkF3DCachedSpecularTexture::New(); + spec->SetFileName(specCachePath.c_str()); + this->EnvMapPrefiltered = spec; + } + + this->GetEnvMapPrefiltered()->HalfPrecisionOff(); +#endif + // HDRI OpenGL this->UseImageBasedLightingOn(); this->SetEnvironmentTexture(hdriTexture); @@ -423,6 +541,65 @@ void vtkF3DRenderer::SetHDRIFile(const std::string& hdriFile) this->AddActor(this->Skybox); this->AutomaticLightCreationOff(); + + // build HDRI textures and spherical harmonics + this->Render(); + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 2, 20221220) + // Create LUT cache file + if (!lutCacheExists) + { + vtkPBRLUTTexture* lut = this->GetEnvMapLookupTable(); + + vtkSmartPointer img = ::SaveTextureToImage( + lut->GetTextureObject(), GL_TEXTURE_2D, 0, lut->GetLUTSize(), VTK_UNSIGNED_SHORT); + + if (img) + { + vtkNew writer; + writer->SetFileName(lutCachePath.c_str()); + writer->SetInputData(img); + writer->Write(); + } + } + + // Create spherical harmonics cache file + if (!shCacheExists) + { + vtkNew table; + table->AddColumn(this->SphericalHarmonics); + + vtkNew writer; + writer->SetInputData(table); + writer->SetFileName(shCachePath.c_str()); + writer->Write(); + } + + // Create specular cache file + if (!specCacheExists) + { + vtkPBRPrefilterTexture* spec = this->GetEnvMapPrefiltered(); + + unsigned int nbLevels = spec->GetPrefilterLevels(); + unsigned int size = spec->GetPrefilterSize(); + + vtkNew mb; + mb->SetNumberOfBlocks(6 * nbLevels); + + for (unsigned int i = 0; i < nbLevels; i++) + { + vtkSmartPointer img = ::SaveTextureToImage( + spec->GetTextureObject(), GL_TEXTURE_CUBE_MAP_POSITIVE_X, i, size >> i, VTK_FLOAT); + + mb->SetBlock(i, img); + } + + vtkNew writer; + writer->SetFileName(specCachePath.c_str()); + writer->SetInputData(mb); + writer->Write(); + } +#endif } else { diff --git a/library/VTKExtensions/Rendering/vtkF3DRenderer.h b/library/VTKExtensions/Rendering/vtkF3DRenderer.h index 2e313ca243..dd7df2d01f 100644 --- a/library/VTKExtensions/Rendering/vtkF3DRenderer.h +++ b/library/VTKExtensions/Rendering/vtkF3DRenderer.h @@ -17,6 +17,7 @@ #include class vtkCornerAnnotation; +class vtkImageData; class vtkOrientationMarkerWidget; class vtkSkybox; class vtkTextActor; @@ -112,6 +113,11 @@ class vtkF3DRenderer : public vtkOpenGLRenderer */ vtkGetVector3Macro(RightVector, double); + /** + * Set cache path + */ + vtkSetMacro(CachePath, std::string); + protected: vtkF3DRenderer(); ~vtkF3DRenderer() override; @@ -184,6 +190,8 @@ class vtkF3DRenderer : public vtkOpenGLRenderer std::string CurrentGridInfo; std::string GridInfo; + + std::string CachePath; }; #endif diff --git a/library/private/window_impl.h b/library/private/window_impl.h index 483e4d1df7..66a8514a22 100644 --- a/library/private/window_impl.h +++ b/library/private/window_impl.h @@ -97,6 +97,12 @@ class window_impl : public window */ virtual vtkRenderWindow* GetRenderWindow(); + /** + * Implementation only API. + * Set the cache path. + */ + void SetCachePath(const std::string& cachePath); + private: class internals; std::unique_ptr Internals; diff --git a/library/public/engine.h b/library/public/engine.h index c894c2b312..cd5fb456bd 100644 --- a/library/public/engine.h +++ b/library/public/engine.h @@ -53,6 +53,16 @@ class F3D_EXPORT engine */ ~engine(); + /** + * Set the cache path. Must be an absolute path. + * Currently, it's only used to store HDRI baked textures. + * By default, the cache path is: + * - Windows: %LOCALAPPDATA%\f3d + * - Linux: ~/.cache/f3d + * - macOS: ~/Library/Caches/f3d + */ + void setCachePath(const std::string& cachePath); + /** * Engine provide a default options that you can use using engine::getOptions(). * But you can use this setter to use other options directly. diff --git a/library/src/engine.cxx b/library/src/engine.cxx index b425ef5f2f..c0d40a0bff 100644 --- a/library/src/engine.cxx +++ b/library/src/engine.cxx @@ -33,10 +33,26 @@ class engine::internals engine::engine(window::Type windowType) : Internals(new engine::internals) { + // build default cache path +#if defined(_WIN32) + std::string cachePath = vtksys::SystemTools::GetEnv("LOCALAPPDATA"); + cachePath = cachePath + "/f3d"; +#else + std::string cachePath = vtksys::SystemTools::GetEnv("HOME"); +#if defined(__APPLE__) + cachePath = cachePath + "/Library/Caches/f3d"; +#elif defined(__unix__) + cachePath = cachePath + "/.cache/f3d"; +#else +#error "Unsupported platform" +#endif +#endif + this->Internals->Options = std::make_unique(); this->Internals->Window = std::make_unique(*this->Internals->Options, windowType); + this->Internals->Window->SetCachePath(cachePath); this->Internals->Loader = std::make_unique(*this->Internals->Options, *this->Internals->Window); @@ -232,6 +248,12 @@ std::vector engine::getReadersInfo() return readersInfo; } +//---------------------------------------------------------------------------- +void engine::setCachePath(const std::string& cachePath) +{ + this->Internals->Window->SetCachePath(cachePath); +} + //---------------------------------------------------------------------------- engine::no_window_exception::no_window_exception(const std::string& what) : exception(what) diff --git a/library/src/window_impl.cxx b/library/src/window_impl.cxx index aa9f91158d..dc09505b6b 100644 --- a/library/src/window_impl.cxx +++ b/library/src/window_impl.cxx @@ -20,6 +20,7 @@ #include #include #include +#include #if F3D_MODULE_EXTERNAL_RENDERING #include @@ -35,12 +36,21 @@ class window_impl::internals { } + std::string GetCachePath() + { + // create directories if they do not exist + vtksys::SystemTools::MakeDirectory(this->CachePath); + + return this->CachePath; + } + std::unique_ptr Camera; vtkSmartPointer RenWin; vtkSmartPointer Renderer; Type WindowType; const options& Options; bool Initialized = false; + std::string CachePath; }; //---------------------------------------------------------------------------- @@ -225,6 +235,8 @@ void window_impl::Initialize(bool withColoring, std::string fileInfo) this->Internals->Renderer = vtkSmartPointer::New(); } + this->Internals->Renderer->SetCachePath(this->Internals->GetCachePath()); + this->Internals->Camera->SetVTKRenderer(this->Internals->Renderer); this->Internals->RenWin->AddRenderer(this->Internals->Renderer); this->Internals->Renderer->Initialize( @@ -392,4 +404,10 @@ void window_impl::InitializeRendererWithColoring(vtkF3DGenericImporter* importer importer->GetPointDataForColoring(), importer->GetCellDataForColoring()); } } + +//---------------------------------------------------------------------------- +void window_impl::SetCachePath(const std::string& cachePath) +{ + this->Internals->CachePath = cachePath; +} }; diff --git a/python/F3DPythonBindings.cxx b/python/F3DPythonBindings.cxx index 6d0d90c6aa..ba3afb3c7b 100644 --- a/python/F3DPythonBindings.cxx +++ b/python/F3DPythonBindings.cxx @@ -187,6 +187,7 @@ PYBIND11_MODULE(f3d, module) .def("getOptions", &f3d::engine::getOptions, py::return_value_policy::reference) .def("setOptions", py::overload_cast(&f3d::engine::setOptions)) .def("getWindow", &f3d::engine::getWindow, py::return_value_policy::reference) + .def("setCachePath", &f3d::engine::setCachePath, "Set the cache path directory") .def_static("loadPlugin", &f3d::engine::loadPlugin, "Load a plugin") .def_static( "autoloadPlugins", &f3d::engine::autoloadPlugins, "Automatically load internal plugins"); diff --git a/testing/baselines/TestHDRI8Bit.png b/testing/baselines/TestHDRI8Bit.png index 7080be1a63..0770c51009 100644 --- a/testing/baselines/TestHDRI8Bit.png +++ b/testing/baselines/TestHDRI8Bit.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bd6d67d5fbc8f9bca59c5686cb7e0e74091d95cb36e9309ce9d6e43d015592b -size 24780 +oid sha256:6ef53d5bd49af970b2da8dbcaccd92c1bb5ef2a49c0a8e71e391abaf8ea58cd7 +size 26149 diff --git a/testing/baselines/TestHDRIEdges.png b/testing/baselines/TestHDRIEdges.png index 6ddf8eb560..0974815923 100644 --- a/testing/baselines/TestHDRIEdges.png +++ b/testing/baselines/TestHDRIEdges.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e0b63489e23d585258ad3e389d3664735dbdd91e672116ea967c641ad0745e7 -size 121377 +oid sha256:e5a00bfedca2d26f28f02fb2a1fa9de15e3fff502bd52a735c5a7d7388e1b73c +size 122163 diff --git a/testing/baselines/TestHDRIOrient.png b/testing/baselines/TestHDRIOrient.png index 4a5478bd26..4ee676703a 100644 --- a/testing/baselines/TestHDRIOrient.png +++ b/testing/baselines/TestHDRIOrient.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3630bd594f9605d9dbb23f599a47a48993c00488562af88ca53418cd300f37f5 -size 81641 +oid sha256:740a50778905c3a101737e58fc763f8fa7e9f0cc9a1b760afc5319e9dddd186a +size 81647