From f585eb7bb356dae25a69f052665bd4ab056d9766 Mon Sep 17 00:00:00 2001 From: Ihtier Date: Tue, 7 Jan 2025 16:30:20 +0300 Subject: [PATCH] Adding OpenUSD example --- .gitignore | 2 + examples/OpenUSD/CMakeLists.txt | 132 +++++++++++++++++++++++ examples/OpenUSD/OpenUSD.patch | 55 ++++++++++ examples/OpenUSD/VEnv.cmake | 29 +++++ examples/OpenUSD/cube.usda | 26 +++++ examples/OpenUSD/main.cpp | 158 ++++++++++++++++++++++++++++ examples/OpenUSD/pyrequirements.txt | Bin 0 -> 166 bytes 7 files changed, 402 insertions(+) create mode 100644 examples/OpenUSD/CMakeLists.txt create mode 100644 examples/OpenUSD/OpenUSD.patch create mode 100644 examples/OpenUSD/VEnv.cmake create mode 100644 examples/OpenUSD/cube.usda create mode 100644 examples/OpenUSD/main.cpp create mode 100644 examples/OpenUSD/pyrequirements.txt diff --git a/.gitignore b/.gitignore index 44abace0..050ae3d1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build/ /build* /.vscode *.DS_Store +install/ +venv/ diff --git a/examples/OpenUSD/CMakeLists.txt b/examples/OpenUSD/CMakeLists.txt new file mode 100644 index 00000000..fe6763ac --- /dev/null +++ b/examples/OpenUSD/CMakeLists.txt @@ -0,0 +1,132 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(CPMExampleOpenUSD) + +# ---- Tools ---- + +macro(find_targets targets dir) + get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES) + foreach(subdir ${subdirectories}) + find_targets(${targets} ${subdir}) + endforeach() + get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS) + list(APPEND ${targets} ${current_targets}) +endmacro() + +function(add_prefix_to_folder prefix) + if(CMAKE_GENERATOR MATCHES "Visual Studio") + foreach(target ${ARGN}) + get_target_property(folder ${target} FOLDER) + if(folder STREQUAL "folder-NOTFOUND") + set(folder "${prefix}") + else() + set(folder "${prefix}/${folder}") + endif() + set_property(TARGET ${target} PROPERTY FOLDER "${folder}") + endforeach() + endif() +endfunction(add_prefix_to_folder) + +# ---- Dependencies ---- + +include(../../cmake/CPM.cmake) + +include(${CMAKE_CURRENT_SOURCE_DIR}/VEnv.cmake) + +# Do not use the latest version (2022.0.0) as it fails to compile. +# The error occurs in openusd\pxr\base\work\dispatcher.cpp at line 36. +CPMAddPackage( + NAME TBB + VERSION 2021.13.0 + GITHUB_REPOSITORY uxlfoundation/oneTBB + OPTIONS + "TBB_TEST OFF") +find_targets(TBB_targets ${TBB_SOURCE_DIR}) +add_prefix_to_folder(TBB ${TBB_targets}) +set(TBB_INCLUDE_DIRS "") +set(TBB_tbb_LIBRARY tbb) + +CPMAddPackage( + NAME OpenSubdiv + VERSION 3.6.0 + GITHUB_REPOSITORY PixarAnimationStudios/OpenSubdiv + GIT_TAG v3_6_0 + OPTIONS + "NO_EXAMPLES ON" + "NO_TUTORIALS ON" + "NO_REGRESSION ON" + "NO_DOC ON" + "NO_TESTS ON") +find_targets(OpenSubdiv_targets ${OpenSubdiv_SOURCE_DIR}) +add_prefix_to_folder(OpenSubdiv ${OpenSubdiv_targets}) +target_include_directories(osd_static_cpu SYSTEM PUBLIC $) +target_include_directories(osd_static_gpu SYSTEM PUBLIC $) +set(OPENSUBDIV_INCLUDE_DIR "") +set(OPENSUBDIV_OSDCPU_LIBRARY osd_static_cpu) +set(OPENSUBDIV_LIBRARIES osd_static_cpu osd_static_gpu) + +CPMAddPackage( + NAME OpenUSD + VERSION 24.11 + GITHUB_REPOSITORY PixarAnimationStudios/OpenUSD + PATCHES "OpenUSD.patch" # Avoid the error "The solution already contains an item named 'boost'" + # and use the latest version of PySide. + OPTIONS + "PXR_BUILD_TESTS OFF" + "PXR_BUILD_EXAMPLES OFF" + "PXR_BUILD_TUTORIALS OFF" + "PXR_BUILD_HTML_DOCUMENTATION OFF") +find_targets(OpenUSD_targets ${OpenUSD_SOURCE_DIR}) +add_prefix_to_folder(OpenUSD ${OpenUSD_targets}) +set(OpenUSD_INCLUDE_DIR "${OpenUSD_BINARY_DIR}/include") +foreach(OpenUSD_target ${OpenUSD_targets}) + get_target_property(OpenUSD_target_TYPE ${OpenUSD_target} TYPE) + if(OpenUSD_target_TYPE STREQUAL "SHARED_LIBRARY") + target_include_directories(${OpenUSD_target} SYSTEM PUBLIC $) + elseif(OpenUSD_target_TYPE STREQUAL "EXECUTABLE") + # It's easier to set the Path than to configure the installation of different libraries + # TODO! Fix install + set_target_properties(${OpenUSD_target} PROPERTIES + VS_DEBUGGER_ENVIRONMENT "PATH=${CMAKE_INSTALL_PREFIX}/bin;${CMAKE_INSTALL_PREFIX}/lib") + elseif(OpenUSD_target_TYPE STREQUAL "UTILITY") + continue() + else() + message(FATAL_ERROR "Unknown target type: ${OpenUSD_target_TYPE} (${OpenUSD_target})") + endif() +endforeach() + +CPMAddPackage( + NAME glfw + VERSION 3.4 + GITHUB_REPOSITORY glfw/glfw + GIT_TAG 3.4 + OPTIONS "BUILD_SHARED_LIBS ON" + "GLFW_BUILD_EXAMPLES OFF" + "GLFW_BUILD_TESTS OFF" + "GLFW_BUILD_DOCS OFF") +find_targets(glfw_targets ${glfw_SOURCE_DIR}) +add_prefix_to_folder(glfw ${glfw_targets}) + +# ---- Executable ---- + +add_executable(CPMExampleOpenUSD main.cpp) +target_compile_features(CPMExampleOpenUSD PRIVATE cxx_std_17) +target_link_libraries(CPMExampleOpenUSD glfw glf hgiGL) + +install(TARGETS CPMExampleOpenUSD RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +if(MSVC) + install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) + # It's easier to set the Path than to configure the installation of different libraries + # TODO! Fix install + set_target_properties(CPMExampleOpenUSD PROPERTIES + VS_DEBUGGER_ENVIRONMENT "PATH=${CMAKE_INSTALL_PREFIX}/bin;${CMAKE_INSTALL_PREFIX}/lib") +endif() + +if(MSVC) + add_custom_target(usdview) + set_target_properties(usdview PROPERTIES + VS_DEBUGGER_COMMAND "${Python3_EXECUTABLE}" + VS_DEBUGGER_COMMAND_ARGUMENTS "${CMAKE_INSTALL_PREFIX}/bin/usdview ${CMAKE_SOURCE_DIR}/cube.usda" + VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin" + VS_DEBUGGER_ENVIRONMENT "PYTHONPATH=$ENV{PYTHONPATH};${CMAKE_INSTALL_PREFIX}/lib/python\nPATH=$ENV{PATH};${CMAKE_INSTALL_PREFIX}/bin;${CMAKE_INSTALL_PREFIX}/lib") +endif() diff --git a/examples/OpenUSD/OpenUSD.patch b/examples/OpenUSD/OpenUSD.patch new file mode 100644 index 00000000..38744ef0 --- /dev/null +++ b/examples/OpenUSD/OpenUSD.patch @@ -0,0 +1,55 @@ +diff --git a/cmake/modules/FindPySide.cmake b/cmake/modules/FindPySide.cmake +index 1fd02ff88..a20b3fe4c 100644 +--- a/cmake/modules/FindPySide.cmake ++++ b/cmake/modules/FindPySide.cmake +@@ -16,7 +16,7 @@ execute_process( + ) + if (pySideImportResult EQUAL 0) + set(pySideImportResult "PySide6") +- set(pySideUIC pyside6-uic python3-pyside6-uic) ++ set(pySideUIC uic pyside6-uic python3-pyside6-uic) + endif() + + # PySide6 not found OR PYSIDE2 explicitly requested +diff --git a/pxr/external/boost/CMakeLists.txt b/pxr/external/boost/CMakeLists.txt +index c34d0bf21..0a1fb5988 100644 +--- a/pxr/external/boost/CMakeLists.txt ++++ b/pxr/external/boost/CMakeLists.txt +@@ -1,5 +1,5 @@ + set(PXR_PREFIX pxr/external) +-set(PXR_PACKAGE boost) ++set(PXR_PACKAGE usd_boost) + + # This subdirectory currently only contains Python-related + # libraries, so skip it entirely if Python support is disabled. +@@ -7,7 +7,7 @@ if (NOT PXR_ENABLE_PYTHON_SUPPORT AND NOT TARGET python_modules) + return() + endif() + +-pxr_library(boost ++pxr_library(usd_boost + PUBLIC_HEADERS + python.hpp + +@@ -22,7 +22,7 @@ pxr_library(boost + # otherwise the build will error out at link time because no + # .lib file will be generated. Using this setting is OK because + # we only expect there to be one exportable symbol in this library. +-set_target_properties(boost ++set_target_properties(usd_boost + PROPERTIES + WINDOWS_EXPORT_ALL_SYMBOLS TRUE + ) +diff --git a/pxr/external/boost/python/CMakeLists.txt b/pxr/external/boost/python/CMakeLists.txt +index ec85789f7..bfdbb3570 100644 +--- a/pxr/external/boost/python/CMakeLists.txt ++++ b/pxr/external/boost/python/CMakeLists.txt +@@ -101,7 +101,7 @@ endif() + + pxr_library(python + LIBRARIES +- boost ++ usd_boost + ${libs} + + INCLUDE_DIRS diff --git a/examples/OpenUSD/VEnv.cmake b/examples/OpenUSD/VEnv.cmake new file mode 100644 index 00000000..ce161ecd --- /dev/null +++ b/examples/OpenUSD/VEnv.cmake @@ -0,0 +1,29 @@ +# https://discourse.cmake.org/t/possible-to-create-a-python-virtual-env-from-cmake-and-then-find-it-with-findpython3/1132 +find_package(Python3 COMPONENTS Interpreter Development REQUIRED) + +set(VENV_PATH "${CMAKE_CURRENT_SOURCE_DIR}/venv") + +if(NOT EXISTS "${VENV_PATH}") + execute_process(COMMAND "${Python3_EXECUTABLE}" -m venv "${VENV_PATH}") +endif() + +set(ENV{VIRTUAL_ENV} "${VENV_PATH}") +set(Python3_FIND_VIRTUALENV FIRST) +unset(Python3_EXECUTABLE) + +find_package(Python3 COMPONENTS Interpreter Development REQUIRED) + +execute_process(COMMAND "${Python3_EXECUTABLE}" + -m pip install + -r "${CMAKE_CURRENT_SOURCE_DIR}/pyrequirements.txt") + +execute_process(COMMAND "${Python3_EXECUTABLE}" + -c "import PySide6; print(PySide6.__file__)" + OUTPUT_VARIABLE PYSIDE_BIN_DIR) + +get_filename_component(PYSIDE_BIN_DIR "${PYSIDE_BIN_DIR}" DIRECTORY) +set(PYSIDE_BIN_DIR "${PYSIDE_BIN_DIR}") + +install(FILES + ${Python3_RUNTIME_LIBRARY_DIRS}/python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}.dll + DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") diff --git a/examples/OpenUSD/cube.usda b/examples/OpenUSD/cube.usda new file mode 100644 index 00000000..a5db2197 --- /dev/null +++ b/examples/OpenUSD/cube.usda @@ -0,0 +1,26 @@ +#usda 1.0 +( + metersPerUnit = 1 + upAxis = "Z" +) + +def Mesh "Cube" ( + active = true +) +{ + uniform bool doubleSided = 1 + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] + int[] faceVertexIndices = [0, 4, 6, 2, 3, 2, 6, 7, 7, 6, 4, 5, 5, 1, 3, 7, 1, 0, 2, 3, 5, 4, 0, 1] + normal3f[] normals = [(0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (-1, 0, 0), (-1, 0, 0), (-1, 0, 0), (-1, 0, 0), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1), (1, 0, 0), (1, 0, 0), (1, 0, 0), (1, 0, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0)] ( + interpolation = "faceVarying" + ) + point3f[] points = [(1, 1, 1), (1, 1, -1), (1, -1, 1), (1, -1, -1), (-1, 1, 1), (-1, 1, -1), (-1, -1, 1), (-1, -1, -1)] + bool[] primvars:sharp_face = [1, 1, 1, 1, 1, 1] ( + interpolation = "uniform" + ) + texCoord2f[] primvars:st = [(0.625, 0.5), (0.875, 0.5), (0.875, 0.75), (0.625, 0.75), (0.375, 0.75), (0.625, 0.75), (0.625, 1), (0.375, 1), (0.375, 0), (0.625, 0), (0.625, 0.25), (0.375, 0.25), (0.125, 0.5), (0.375, 0.5), (0.375, 0.75), (0.125, 0.75), (0.375, 0.5), (0.625, 0.5), (0.625, 0.75), (0.375, 0.75), (0.375, 0.25), (0.625, 0.25), (0.625, 0.5), (0.375, 0.5)] ( + interpolation = "faceVarying" + ) + uniform token subdivisionScheme = "none" +} diff --git a/examples/OpenUSD/main.cpp b/examples/OpenUSD/main.cpp new file mode 100644 index 00000000..309951ae --- /dev/null +++ b/examples/OpenUSD/main.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include +#include + +// GLFW (include after pxr/imaging/garch/glApi.h) +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +static const int _imgSize = 512; +static const HgiFormat _imgFormat = HgiFormatUNorm8Vec4; +static const HioFormat _imgHioFormat = HioFormatUNorm8Vec4; + +void _SaveToPNG(int width, int height, const char* pixels, const std::string& filePath) { + HioImage::StorageSpec storage; + storage.width = width; + storage.height = height; + storage.format = _imgHioFormat; + storage.flipped = false; + storage.data = (void*)pixels; + + HioImageSharedPtr image = HioImage::OpenForWriting(filePath); + TF_VERIFY(image && image->Write(storage)); +} + +void _SaveGpuTextureToFile(HgiGL& hgiGL, HgiTextureHandle const& texHandle, int width, int height, + HgiFormat format, std::string const& filePath) { + // Copy the pixels from gpu into a cpu buffer so we can save it to disk. + const size_t bufferByteSize = width * height * HgiGetDataSizeOfFormat(format); + std::vector buffer(bufferByteSize); + + HgiTextureGpuToCpuOp copyOp; + copyOp.gpuSourceTexture = texHandle; + copyOp.sourceTexelOffset = GfVec3i(0); + copyOp.mipLevel = 0; + copyOp.cpuDestinationBuffer = buffer.data(); + copyOp.destinationByteOffset = 0; + copyOp.destinationBufferByteSize = bufferByteSize; + + HgiBlitCmdsUniquePtr blitCmds = hgiGL.CreateBlitCmds(); + blitCmds->CopyTextureGpuToCpu(copyOp); + hgiGL.SubmitCmds(blitCmds.get(), HgiSubmitWaitTypeWaitUntilCompleted); + + _SaveToPNG(width, height, buffer.data(), filePath); +} + +HgiGraphicsCmdsDesc _CreateGraphicsCmdsColor0Color1Depth(HgiGL& hgiGL, GfVec3i const& size, + HgiFormat colorFormat) { + // Create two color attachments + HgiTextureDesc texDesc; + texDesc.dimensions = size; + texDesc.type = HgiTextureType2D; + texDesc.format = colorFormat; + texDesc.sampleCount = HgiSampleCount1; + texDesc.usage = HgiTextureUsageBitsColorTarget; + HgiTextureHandle colorTex0 = hgiGL.CreateTexture(texDesc); + HgiTextureHandle colorTex1 = hgiGL.CreateTexture(texDesc); + + // Create a depth attachment + texDesc.usage = HgiTextureUsageBitsDepthTarget; + texDesc.format = HgiFormatFloat32; + HgiTextureHandle depthTex = hgiGL.CreateTexture(texDesc); + + // Setup color and depth attachments + HgiAttachmentDesc colorAttachment0; + colorAttachment0.loadOp = HgiAttachmentLoadOpClear; + colorAttachment0.storeOp = HgiAttachmentStoreOpStore; + colorAttachment0.format = colorFormat; + colorAttachment0.usage = HgiTextureUsageBitsColorTarget; + + HgiAttachmentDesc colorAttachment1; + colorAttachment1.loadOp = HgiAttachmentLoadOpClear; + colorAttachment1.storeOp = HgiAttachmentStoreOpStore; + colorAttachment1.format = colorFormat; + colorAttachment1.usage = HgiTextureUsageBitsColorTarget; + + HgiAttachmentDesc depthAttachment; + depthAttachment.format = HgiFormatFloat32; + depthAttachment.usage = HgiTextureUsageBitsDepthTarget; + + // Configure graphics cmds + HgiGraphicsCmdsDesc desc; + desc.colorAttachmentDescs.push_back(colorAttachment0); + desc.colorAttachmentDescs.push_back(colorAttachment1); + desc.depthAttachmentDesc = depthAttachment; + desc.colorTextures.push_back(colorTex0); + desc.colorTextures.push_back(colorTex1); + desc.depthTexture = depthTex; + + return desc; +} + +bool TestGraphicsCmdsClear() { + HgiGL hgiGL; + + const size_t width = _imgSize; + const size_t height = _imgSize; + const HgiFormat format = _imgFormat; + + // Create a default cmds description and set the clearValue for the + // first attachment to something other than black. + // Setting 'loadOp' tp 'Clear' is important for this test since we expect + // the attachment to be cleared when the graphics cmds is created. + HgiGraphicsCmdsDesc desc + = _CreateGraphicsCmdsColor0Color1Depth(hgiGL, GfVec3i(width, height, 1), format); + desc.colorAttachmentDescs[0].loadOp = HgiAttachmentLoadOpClear; + desc.colorAttachmentDescs[0].storeOp = HgiAttachmentStoreOpStore; + desc.colorAttachmentDescs[0].clearValue = GfVec4f(1, 0, 0.5, 1); + + // We expect attachment0 to be cleared when the cmds is created via + // the loadOp property in desc. + HgiGraphicsCmdsUniquePtr gfxCmds = hgiGL.CreateGraphicsCmds(desc); + hgiGL.SubmitCmds(gfxCmds.get()); + + // Save attachment0 to disk + _SaveGpuTextureToFile(hgiGL, desc.colorTextures[0], width, height, format, + "CPMExampleOpenUSD.png"); + + // Cleanup + for (HgiTextureHandle& tex : desc.colorTextures) { + hgiGL.DestroyTexture(&tex); + } + if (desc.depthTexture) { + hgiGL.DestroyTexture(&desc.depthTexture); + } + + return true; +} + +class ScopeExit { +public: + explicit ScopeExit(const std::function& f) : func(f) {} + ~ScopeExit() { func(); } + +private: + std::function func; +}; + +int main(int argc, char** argv) { + if (!glfwInit()) return EXIT_FAILURE; + + // Offscreen contexts (https://www.glfw.org/docs/latest/context.html#context_offscreen) + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + GLFWwindow* window = glfwCreateWindow(100, 100, "CPMExampleOpenUSD", NULL, NULL); + glfwMakeContextCurrent(window); + ScopeExit onExit([window]() { glfwDestroyWindow(window); }); + + if (!GarchGLApiLoad()) return EXIT_FAILURE; + GlfRegisterDefaultDebugOutputMessageCallback(); + GlfSharedGLContextScopeHolder sharedContext; + GlfContextCaps::InitInstance(); + if (!TestGraphicsCmdsClear()) return EXIT_FAILURE; + return EXIT_SUCCESS; +} diff --git a/examples/OpenUSD/pyrequirements.txt b/examples/OpenUSD/pyrequirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..4407e5e4ae865346301049b16fc1828add60dfb7 GIT binary patch literal 166 zcmY+8!3u&v6h+TE@K=U1#&Od+5JAB&2y#)_6pSwB=d0^;C6Bv#=iJBpeS0zk5AKXq zTr?GqS~*d0VC5Yj(a4q7jI&WkFr!gB^5jOjquyvzb7s+nnYU&lnHBK55|pEPQO-+N LHd<=GX8Yj}o&XpH literal 0 HcmV?d00001