Files
UsdLayerManager/CMakeLists.txt
T
indigo 09819091c4 Add Hypershade-style Material Editor with node graph and shader-ball preview
Browser column lists all scene materials plus a searchable create-node
list; Show Graph (or double-click) loads a material into the node-graph
work area. The graph shows the entire network including MaterialX
(.mtlx) node graphs, resolving connections through NodeGraph boundaries
via UsdShadeUtils::GetValueProducingAttributes, with layered auto-layout
for nodes lacking authored uiPosition. Canvas navigates viewport-style
(Alt+MMB pan / Alt+RMB zoom) and TAB opens a Nuke-style search popup.
Selecting a node shows a typed property editor (live-apply, one undo
command per edit) and previews that node's output on the shader ball.

The preview renders through its own Hydra engine into a scratch stage
that composes the material via a reference to the source root layer (so
referenced .mtlx materials work), re-renders until progressive delegates
(Arnold/Cycles/Embree) converge, and lights with HDR dome presets
(External/Room/Interior/Sunset; CC0 Poly Haven EXRs fetched at CMake
configure). texture:format is authored latlong explicitly - left
automatic, hdArnold falls back to Arnold's angular fisheye default -
and hdCycles gets a -90 X pole rotation to match Storm's +Y-pole
sampling.

Mutations go through new ICommand subclasses (create shader node,
connect/disconnect attrs). imgui-node-editor is vendored (gitignored)
with a local one-line patch: c_ScrollButtonIndex 1->2 for MMB pan.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 20:44:54 +08:00

794 lines
32 KiB
CMake

cmake_minimum_required(VERSION 3.20)
project(UsdLayerManager VERSION 1.0.0 LANGUAGES C CXX)
# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Add cmake modules path
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
# Find dependencies
find_package(OpenGL REQUIRED)
find_package(OpenUSD REQUIRED)
find_package(Imgui REQUIRED)
find_package(ImguiNodeEditor REQUIRED)
find_package(Glad REQUIRED)
find_package(FFmpeg REQUIRED)
# OpenColorIO — provided by the OpenUSD distribution (USD built with OCIO support)
set(OpenColorIO_DIR
"${OpenUSD_ROOT_DIR}/lib/cmake/OpenColorIO"
CACHE PATH "OpenColorIO CMake config dir" FORCE)
find_package(OpenColorIO CONFIG REQUIRED)
# USD build that includes hdEmbree (built with PXR_ENABLE_EMBREE_PLUGIN=ON).
# Can be a separate install from OpenUSD_ROOT_DIR; leave empty to search there instead.
set(HDEMBREE_USD_ROOT "" CACHE PATH "USD build containing hdEmbree plugin (separate from OpenUSD_ROOT_DIR)")
# Only needed if embree*.dll are not inside HDEMBREE_USD_ROOT/bin.
set(EMBREE_LOCATION "" CACHE PATH "Embree SDK bin/ directory (leave empty if bundled in HDEMBREE_USD_ROOT/bin)")
# ── hdEmbree detection ───────────────────────────────────────────────────────
if(HDEMBREE_USD_ROOT AND EXISTS "${HDEMBREE_USD_ROOT}")
set(_hdembree_root "${HDEMBREE_USD_ROOT}")
else()
set(_hdembree_root "${OpenUSD_ROOT_DIR}")
endif()
# hdEmbree.dll lives at plugin/usd/hdEmbree.dll; the subdir holds only plugin metadata.
set(HDEMBREE_PLUGIN_DIR "${_hdembree_root}/plugin/usd/hdEmbree")
set(HDEMBREE_DLL "${_hdembree_root}/plugin/usd/hdEmbree.dll")
if(EXISTS "${HDEMBREE_PLUGIN_DIR}")
set(OPENUSD_HAS_EMBREE TRUE)
if(EMBREE_LOCATION AND EXISTS "${EMBREE_LOCATION}")
set(_embree_dll_dir "${EMBREE_LOCATION}")
else()
set(_embree_dll_dir "${_hdembree_root}/bin")
endif()
# Skip tbb*.dll — already supplied by the main USD install to avoid conflicts.
file(GLOB EMBREE_DLLS "${_embree_dll_dir}/embree*.dll")
else()
set(OPENUSD_HAS_EMBREE FALSE)
message(STATUS "hdEmbree: NOT found — set HDEMBREE_USD_ROOT to a USD build with PXR_ENABLE_EMBREE_PLUGIN=ON")
endif()
# ── hdArnold detection ───────────────────────────────────────────────────────
# HDARNOLD_ROOT: CMAKE_INSTALL_PREFIX used when building arnold-usd
set(HDARNOLD_ROOT "" CACHE PATH "arnold-usd install directory (the one containing plugin/hdArnold.dll)")
# ARNOLD_LOCATION: Arnold SDK root (MtoA dir) — needed to deploy ai.dll
set(ARNOLD_LOCATION "" CACHE PATH "Arnold SDK root (e.g. C:/Autodesk/mtoa/5.5.0/2026) for ai.dll deployment")
if(HDARNOLD_ROOT AND EXISTS "${HDARNOLD_ROOT}/plugin/hdArnold.dll")
set(OPENUSD_HAS_ARNOLD TRUE)
set(HDARNOLD_DLL "${HDARNOLD_ROOT}/plugin/hdArnold.dll")
set(NDRARNOLD_DLL "${HDARNOLD_ROOT}/plugin/ndrArnold.dll")
set(HDARNOLD_PLUGIN_DIR "${HDARNOLD_ROOT}/plugin/hdArnold")
set(NDRARNOLD_PLUGIN_DIR "${HDARNOLD_ROOT}/plugin/ndrArnold")
if(ARNOLD_LOCATION AND EXISTS "${ARNOLD_LOCATION}/bin/ai.dll")
set(ARNOLD_AI_DLL "${ARNOLD_LOCATION}/bin/ai.dll")
# ai.dll also requires companion runtime DLLs (OIDN denoiser, Adsk licensing, cer)
file(GLOB ARNOLD_RUNTIME_DLLS
"${ARNOLD_LOCATION}/bin/ai*.dll"
"${ARNOLD_LOCATION}/bin/AdClm*.dll"
"${ARNOLD_LOCATION}/bin/AdskLicensing*.dll"
"${ARNOLD_LOCATION}/bin/cer.dll"
)
else()
set(ARNOLD_AI_DLL "")
set(ARNOLD_RUNTIME_DLLS "")
endif()
else()
set(OPENUSD_HAS_ARNOLD FALSE)
message(STATUS "hdArnold: NOT found — set HDARNOLD_ROOT to the arnold-usd install dir and ARNOLD_LOCATION to the MtoA root")
endif()
# ── Cycles hdCycles Hydra render delegate ────────────────────────────────────
# Build Blender Cycles from third_party/cycles using ExternalProject.
# Set WITH_CYCLES=ON to enable; requires MSVC, and uses the precompiled libs
# bundled in third_party/cycles/lib/windows_x64/.
# hdCycles.dll is deployed to usd/ alongside hdEmbree and hdArnold.
option(WITH_CYCLES "Build Cycles Hydra render delegate from third_party/cycles" OFF)
set(CYCLES_BINARY_DIR "${CMAKE_BINARY_DIR}/cycles_build")
set(CYCLES_INSTALL_DIR "${CMAKE_BINARY_DIR}/cycles_install")
set(HDCYCLES_DLL "${CYCLES_INSTALL_DIR}/hydra/hdCycles.dll")
set(HDCYCLES_PLUGIN_DIR "${CYCLES_INSTALL_DIR}/hydra/hdCycles")
# tbb12.dll is required by openvdb/embree4 but lives in the precompiled lib tree,
# not in the install output — deploy it explicitly.
set(CYCLES_TBB12_DLL "${CMAKE_SOURCE_DIR}/third_party/cycles/lib/windows_x64/tbb/bin/tbb12.dll")
if(WITH_CYCLES)
include(ExternalProject)
# Resolve absolute USD root so we can forward it to the sub-build.
# FindOpenUSD.cmake stores the path in OpenUSD_ROOT_DIR.
if(NOT OpenUSD_ROOT_DIR)
message(FATAL_ERROR "WITH_CYCLES requires OpenUSD_ROOT_DIR to be set (run find_package(OpenUSD) first).")
endif()
ExternalProject_Add(CyclesBuild
SOURCE_DIR "${CMAKE_SOURCE_DIR}/third_party/cycles"
BINARY_DIR "${CYCLES_BINARY_DIR}"
INSTALL_DIR "${CYCLES_INSTALL_DIR}"
CMAKE_ARGS
# Always Release — Cycles' Debug build is slow and rarely needed here.
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
# Point Cycles at our app's USD so hdCycles is ABI-compatible.
-DPXR_ROOT=${OpenUSD_ROOT_DIR}
# Hydra delegate is the only deliverable we need.
-DWITH_CYCLES_HYDRA_RENDER_DELEGATE=ON
-DWITH_CYCLES_USD=ON
# Use bundled precompiled libs from lib/windows_x64/.
-DWITH_LIBS_PRECOMPILED=ON
# Turn off heavy optional features that aren't needed for the delegate.
-DWITH_CYCLES_STANDALONE_GUI=OFF
-DWITH_CYCLES_DEVICE_CUDA=OFF
-DWITH_CYCLES_DEVICE_OPTIX=OFF
-DWITH_CYCLES_DEVICE_HIP=OFF
-DWITH_CYCLES_DEVICE_ONEAPI=OFF
-DWITH_CYCLES_LOGGING=OFF
# USD ships OpenVDB v9; Cycles prebuilt is v13 — ABI mismatch, both named
# openvdb.dll. Disable openvdb in Cycles to avoid the version conflict.
-DWITH_CYCLES_OPENVDB=OFF
# Embree4 links tbb12.dll; USD already loads tbb.dll at process startup.
# oneTBB detects the older tbb.dll and aborts initialization (DLL_INIT_FAILED).
# Disable Embree so Cycles uses its own BVH traversal instead.
-DWITH_CYCLES_EMBREE=OFF
# ---- pxrConfig.cmake dependency overrides ----
# pxrConfig.cmake runs find_dependency() for these before Cycles'
# precompiled lib paths are on CMAKE_PREFIX_PATH, so we must supply
# them explicitly. The Python3 vars also override the hardcoded
# CI build-machine path embedded in pxrConfig.cmake.
-DPython3_EXECUTABLE=$ENV{LOCALAPPDATA}/Programs/Python/Python312/python.exe
-DPython3_LIBRARY=$ENV{LOCALAPPDATA}/Programs/Python/Python312/libs/python312.lib
-DPython3_INCLUDE_DIR=$ENV{LOCALAPPDATA}/Programs/Python/Python312/include
-DTBB_DIR=${CMAKE_SOURCE_DIR}/third_party/cycles/lib/windows_x64/tbb/lib/cmake/TBB
-DMaterialX_DIR=${OpenUSD_ROOT_DIR}/lib/cmake/MaterialX
# OpenSubdiv and Imath cmake configs are bundled in the USD install.
# We must supply their dirs so pxrTargets.cmake can resolve the imported targets.
-DOpenSubdiv_DIR=${OpenUSD_ROOT_DIR}/lib/cmake/OpenSubdiv
-DImath_DIR=${OpenUSD_ROOT_DIR}/lib/cmake/Imath
# Force Cycles to use its own prebuilt OCIO 2.5, not the OCIO 2.1
# bundled in the new USD include dir. Without this, the new USD's
# include/OpenColorIO/ (OCIO 2.1) shadows Cycles' prebuilt OCIO 2.5
# headers and the colorspace.cpp API calls fail to compile.
-DOpenColorIO_DIR=${CMAKE_SOURCE_DIR}/third_party/cycles/lib/windows_x64/opencolorio/lib/cmake/OpenColorIO
BUILD_COMMAND
${CMAKE_COMMAND} --build <BINARY_DIR> --config Release
INSTALL_COMMAND
${CMAKE_COMMAND} --install <BINARY_DIR> --config Release
BUILD_BYPRODUCTS
"${HDCYCLES_DLL}"
)
set(OPENUSD_HAS_CYCLES TRUE)
else()
set(OPENUSD_HAS_CYCLES FALSE)
message(STATUS "hdCycles: disabled — set WITH_CYCLES=ON to build from third_party/cycles")
endif()
# Collect source files
file(GLOB_RECURSE CORE_SOURCES "src/core/*.cpp")
file(GLOB_RECURSE UI_SOURCES "src/ui/*.cpp")
file(GLOB_RECURSE UTILS_SOURCES "src/utils/*.cpp")
set(MAIN_SOURCE "src/main.cpp")
# Create executable
add_executable(UsdLayerManager
${MAIN_SOURCE}
${CORE_SOURCES}
${UI_SOURCES}
${UTILS_SOURCES}
)
# Include directories
target_include_directories(UsdLayerManager PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/third_party/fontawesome
${CMAKE_SOURCE_DIR}/third_party/nanosvg
)
# Link libraries
target_link_libraries(UsdLayerManager PRIVATE
OpenUSD::OpenUSD
Imgui::Imgui
ImguiNodeEditor::ImguiNodeEditor
Glad::Glad
FFmpeg::FFmpeg
OpenColorIO::OpenColorIO
ole32
shell32
)
target_include_directories(UsdLayerManager PRIVATE
"${CMAKE_SOURCE_DIR}/third_party/ffmpeg/include"
)
# Cycles is an ExternalProject; UsdLayerManager must wait for it to finish
# before POST_BUILD copies hdCycles.dll to the output directory.
if(WITH_CYCLES)
add_dependencies(UsdLayerManager CyclesBuild)
endif()
# Platform-specific settings
if(WIN32)
# Windows-specific settings
target_compile_definitions(UsdLayerManager PRIVATE
WIN32_LEAN_AND_MEAN
NOMINMAX
_CRT_SECURE_NO_WARNINGS
)
# OpenColorIO DLL — copy from USD bin/ (which already ships OpenColorIO_2_1.dll)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${OpenUSD_ROOT_DIR}/bin/OpenColorIO_2_1.dll"
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying OpenColorIO runtime DLL..."
)
# FFmpeg DLLs — copy all .dll files from third_party/ffmpeg/bin
file(GLOB FFMPEG_RUNTIME_DLLS "${CMAKE_SOURCE_DIR}/third_party/ffmpeg/bin/*.dll")
if(FFMPEG_RUNTIME_DLLS)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${FFMPEG_RUNTIME_DLLS}
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying FFmpeg runtime DLLs..."
)
endif()
# cmake -P script to copy USD bin/ DLLs excluding CRT DLLs (msvcp, vcruntime, etc.).
# The USD distribution bundles older CRT DLLs (14.34) that conflict with Cycles
# binaries built against the newer system CRT (14.42+). The system CRT is
# always present on Windows 10+ — no need to bundle our own copy.
file(WRITE "${CMAKE_BINARY_DIR}/copy_usd_bin_dlls.cmake"
"file(GLOB _all_dlls \"${OpenUSD_ROOT_DIR}/bin/*.dll\")\n"
"set(_dlls)\n"
"foreach(_dll \${_all_dlls})\n"
" get_filename_component(_name \"\${_dll}\" NAME)\n"
" string(TOLOWER \"\${_name}\" _lname)\n"
" if(_lname MATCHES \"^(msvcp|vcruntime|ucrtbase|concrt|api-ms-win-)\")\n"
" continue()\n"
" endif()\n"
" list(APPEND _dlls \"\${_dll}\")\n"
"endforeach()\n"
"if(_dlls)\n"
" file(COPY \${_dlls} DESTINATION \"\${DEST_DIR}\")\n"
"endif()\n"
)
# Copy runtime DLLs to output directory for each configuration
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:UsdLayerManager>"
# OpenUSD lib/ — usd_*.dll, boost_*.dll, Alembic.dll, etc.
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_BIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>"
# OpenUSD bin/ — tbb.dll, MaterialX, OpenEXR, OpenImageIO, etc. (CRT DLLs excluded)
COMMAND ${CMAKE_COMMAND}
"-DDEST_DIR=$<TARGET_FILE_DIR:UsdLayerManager>"
-P "${CMAKE_BINARY_DIR}/copy_usd_bin_dlls.cmake"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/lib/usd"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/plugin/usd"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python312.dll"
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python3.dll"
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying runtime DLLs and USD plugins..."
)
if(OPENUSD_HAS_EMBREE)
add_custom_command(TARGET UsdLayerManager POST_BUILD
# plugInfo.json has LibraryPath "../hdEmbree.dll" relative to usd/hdEmbree/,
# which resolves to usd/hdEmbree.dll — so the DLL must live in usd/, not exe root.
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${HDEMBREE_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdEmbree.dll"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${HDEMBREE_PLUGIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdEmbree"
COMMENT "Copying hdEmbree plugin..."
)
if(EMBREE_DLLS)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${EMBREE_DLLS}
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying Embree runtime DLLs..."
)
endif()
endif()
if(OPENUSD_HAS_ARNOLD)
add_custom_command(TARGET UsdLayerManager POST_BUILD
# DLLs go in usd/ alongside their metadata dirs (LibraryPath "../hdArnold.dll")
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${HDARNOLD_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdArnold.dll"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${NDRARNOLD_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/ndrArnold.dll"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${HDARNOLD_PLUGIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdArnold"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${NDRARNOLD_PLUGIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/ndrArnold"
COMMENT "Copying hdArnold plugin..."
)
if(ARNOLD_RUNTIME_DLLS)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${ARNOLD_RUNTIME_DLLS}
"$<TARGET_FILE_DIR:UsdLayerManager>"
COMMENT "Copying Arnold runtime DLLs (ai, OIDN, licensing)..."
)
endif()
endif()
if(OPENUSD_HAS_CYCLES)
# hdCycles uses the same usd/ layout as hdEmbree:
# usd/hdCycles.dll + usd/hdCycles/resources/plugInfo.json
# The bundled plugInfo.json has Root=".." and LibraryPath="../hdCycles.dll"
# which resolves correctly once the plugin dir lives in usd/.
#
# Runtime deps (embree4, epoxy, imath, openvdb, OIDN, sycl, vulkan, etc.)
# live in CYCLES_INSTALL_DIR root and must be next to the exe. We can't
# file(GLOB) them at configure time (ExternalProject hasn't run yet), so we
# write a small cmake -P script that globs them at build time instead.
# The script runs at build time (after ExternalProject finishes) so it can glob
# the actual install output. CRT/Windows DLLs are excluded — they must come
# from the system or the main exe's toolchain, not from Cycles' bundled copy.
file(WRITE "${CMAKE_BINARY_DIR}/copy_cycles_runtime_dlls.cmake"
"file(GLOB _all_dlls \"${CYCLES_INSTALL_DIR}/*.dll\")\n"
"set(_dlls)\n"
"foreach(_dll \${_all_dlls})\n"
" get_filename_component(_name \"\${_dll}\" NAME)\n"
" string(TOLOWER \"\${_name}\" _lname)\n"
# Exclude CRT DLLs (system must supply these; Cycles bundles an older version).
" if(_lname MATCHES \"^(msvcp|vcruntime|ucrtbase|concrt|api-ms-win-)\")\n"
" continue()\n"
" endif()\n"
# Exclude debug-variant DLLs (not needed in release, just wasted space).
# Pattern: OpenColorIO_d_2_5.dll → contains "_d_"
# openjph.0.25d.dll → ends with "d.dll"
" if(_lname MATCHES \"_d_\" OR _lname MATCHES \"d[.]dll$\")\n"
" continue()\n"
" endif()\n"
" list(APPEND _dlls \"\${_dll}\")\n"
"endforeach()\n"
"if(_dlls)\n"
" file(COPY \${_dlls} DESTINATION \"\${DEST_DIR}\")\n"
"endif()\n"
)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${HDCYCLES_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdCycles.dll"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${HDCYCLES_PLUGIN_DIR}"
"$<TARGET_FILE_DIR:UsdLayerManager>/usd/hdCycles"
COMMAND ${CMAKE_COMMAND}
"-DDEST_DIR=$<TARGET_FILE_DIR:UsdLayerManager>"
-P "${CMAKE_BINARY_DIR}/copy_cycles_runtime_dlls.cmake"
# tbb12.dll is required by openvdb/embree4 but is not in the install output
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CYCLES_TBB12_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/tbb12.dll"
COMMENT "Copying hdCycles plugin and runtime DLLs..."
)
endif()
endif()
# Compiler warnings
if(MSVC)
target_compile_options(UsdLayerManager PRIVATE /W4)
else()
target_compile_options(UsdLayerManager PRIVATE -Wall -Wextra -Wpedantic)
endif()
# Install rules
install(TARGETS UsdLayerManager
RUNTIME DESTINATION bin
)
# Install all OpenUSD runtime DLLs (same set that POST_BUILD copies to build/Release).
# Without these the exe cannot start from install/bin.
install(DIRECTORY ${OpenUSD_BIN_DIR}/
DESTINATION bin
FILES_MATCHING PATTERN "*.dll"
)
# Install OpenUSD bin/ DLLs — tbb.dll, tbbmalloc.dll, MaterialX, OpenEXR,
# OpenImageIO, zlib, etc. Exclude CRT DLLs: the system VC++ Redistributable supplies
# these at runtime; bundling an older copy (14.34) causes crashes on machines with
# Cycles or other DLLs compiled against the newer CRT (14.42+).
install(DIRECTORY ${OpenUSD_ROOT_DIR}/bin/
DESTINATION bin
FILES_MATCHING PATTERN "*.dll"
PATTERN "msvcp*" EXCLUDE
PATTERN "vcruntime*" EXCLUDE
PATTERN "ucrtbase*" EXCLUDE
PATTERN "concrt*" EXCLUDE
PATTERN "api-ms-win-*" EXCLUDE
)
install(DIRECTORY ${OpenUSD_ROOT_DIR}/lib/usd
DESTINATION bin
)
install(DIRECTORY ${OpenUSD_ROOT_DIR}/plugin/usd/
DESTINATION bin/usd
)
if(OPENUSD_HAS_EMBREE)
# Install to bin/usd/ so plugInfo.json's LibraryPath "../hdEmbree.dll" resolves correctly.
install(FILES "${HDEMBREE_DLL}"
DESTINATION bin/usd
OPTIONAL
)
install(DIRECTORY "${HDEMBREE_PLUGIN_DIR}"
DESTINATION bin/usd
OPTIONAL
)
if(EMBREE_DLLS)
install(FILES ${EMBREE_DLLS}
DESTINATION bin
OPTIONAL
)
endif()
endif()
if(OPENUSD_HAS_ARNOLD)
install(FILES "${HDARNOLD_DLL}" "${NDRARNOLD_DLL}"
DESTINATION bin/usd
OPTIONAL
)
install(DIRECTORY "${HDARNOLD_PLUGIN_DIR}"
DESTINATION bin/usd
OPTIONAL
)
install(DIRECTORY "${NDRARNOLD_PLUGIN_DIR}"
DESTINATION bin/usd
OPTIONAL
)
if(ARNOLD_RUNTIME_DLLS)
install(FILES ${ARNOLD_RUNTIME_DLLS}
DESTINATION bin
OPTIONAL
)
endif()
endif()
if(OPENUSD_HAS_CYCLES)
install(FILES "${HDCYCLES_DLL}"
DESTINATION bin/usd
OPTIONAL
)
install(DIRECTORY "${HDCYCLES_PLUGIN_DIR}"
DESTINATION bin/usd
OPTIONAL
)
# Cycles runtime deps (embree4, epoxy, imath, openvdb, OIDN, sycl, etc.).
# No OPTIONAL: install(DIRECTORY) disallows it with FILES_MATCHING, and the
# dir always exists here since it is produced by the CyclesBuild step.
#
# EXCLUDE the MSVC CRT and api-ms-win DLLs that Cycles bundles: they are
# older than what the system ships (v14.34 vs system v14.42+), and placing
# an older MSVCP140.dll/VCRUNTIME140.dll ahead of System32 on the DLL
# search path causes sycl8.dll (built with newer CRT) to fail DllMain with
# ERROR_DLL_INIT_FAILED (1114) when hdCycles.dll is loaded.
# The system VC++ Redistributable always satisfies these deps at runtime.
install(DIRECTORY "${CYCLES_INSTALL_DIR}/"
DESTINATION bin
FILES_MATCHING
PATTERN "*.dll"
PATTERN "MSVCP140*.dll" EXCLUDE
PATTERN "VCRUNTIME140*.dll" EXCLUDE
PATTERN "ucrtbase*.dll" EXCLUDE
PATTERN "api-ms-win-*.dll" EXCLUDE
PATTERN "OpenColorIO_d_*.dll" EXCLUDE # debug OCIO build
PATTERN "openjph.*d.dll" EXCLUDE # debug OpenJPH
)
# tbb12.dll (used by openvdb/embree4) lives in the precompiled lib tree, not install output
install(FILES "${CYCLES_TBB12_DLL}"
DESTINATION bin
OPTIONAL
)
endif()
# Install Python runtime DLLs required by OpenUSD and Cycles.
# python312.dll : linked by Cycles (hdCycles.dll → python312.dll)
# python311.dll : linked by usd_python.dll (the OpenUSD Python bindings were
# built against Python 3.11, regardless of what we compile with)
# Detect python311.dll from common install paths.
foreach(_pyroot
"$ENV{LOCALAPPDATA}/Programs/Python/Python311"
"C:/Python311"
"C:/Program Files/Python311"
)
if(EXISTS "${_pyroot}/python311.dll")
set(PYTHON311_DLL "${_pyroot}/python311.dll")
message(STATUS "Found python311.dll (required by usd_python.dll): ${PYTHON311_DLL}")
break()
endif()
endforeach()
set(_python_runtime_dlls
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python312.dll"
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python3.dll"
)
if(PYTHON311_DLL)
list(APPEND _python_runtime_dlls "${PYTHON311_DLL}")
endif()
install(FILES ${_python_runtime_dlls}
DESTINATION bin
OPTIONAL # OPTIONAL so the install doesn't fail on machines with a
# system-wide Python; in that case the DLL is on PATH already.
)
# Also copy python311.dll to the build output directory so debug runs work
# without requiring C:\Python311 to be on PATH.
if(PYTHON311_DLL)
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PYTHON311_DLL}"
"$<TARGET_FILE_DIR:UsdLayerManager>/python311.dll"
COMMENT "Copying python311.dll (for usd_python.dll)..."
)
endif()
# ---------------------------------------------------------------------------
# OCIO: download ACES 1.2 config at configure time if not present
# ---------------------------------------------------------------------------
set(_ocio_config_dir "${CMAKE_SOURCE_DIR}/resources/OpenColorIO-Configs/aces_1.2")
if(NOT EXISTS "${_ocio_config_dir}/config.ocio")
message(STATUS "Downloading ACES 1.2 OCIO config (~124 MB) ...")
set(_ocio_zip "${CMAKE_BINARY_DIR}/aces_1.2.zip")
file(DOWNLOAD
"https://github.com/colour-science/OpenColorIO-Configs/releases/download/v1.2/OpenColorIO-Config-ACES-1.2.zip"
"${_ocio_zip}"
SHOW_PROGRESS
STATUS _ocio_dl_status
)
list(GET _ocio_dl_status 0 _ocio_dl_code)
if(_ocio_dl_code EQUAL 0)
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xf "${_ocio_zip}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
file(COPY "${CMAKE_BINARY_DIR}/OpenColorIO-Config-ACES-1.2/aces_1.2/"
DESTINATION "${_ocio_config_dir}")
file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/OpenColorIO-Config-ACES-1.2")
file(REMOVE "${_ocio_zip}")
message(STATUS "ACES 1.2 OCIO config installed to ${_ocio_config_dir}")
else()
message(WARNING "ACES 1.2 OCIO download failed (${_ocio_dl_status}) — OCIO will not be bundled.")
endif()
endif()
# ---------------------------------------------------------------------------
# HDRI: shader-ball lighting presets (Poly Haven, CC0) at configure time
# ---------------------------------------------------------------------------
set(_hdri_dir "${CMAKE_SOURCE_DIR}/resources/hdri")
set(_hdri_files
kloofendal_48d_partly_cloudy_puresky_1k.exr
lebombo_1k.exr
artist_workshop_1k.exr
venice_sunset_1k.exr
)
file(MAKE_DIRECTORY "${_hdri_dir}")
foreach(_hdri ${_hdri_files})
if(NOT EXISTS "${_hdri_dir}/${_hdri}")
message(STATUS "Downloading HDRI ${_hdri} ...")
file(DOWNLOAD
"https://dl.polyhaven.org/file/ph-assets/HDRIs/exr/1k/${_hdri}"
"${_hdri_dir}/${_hdri}"
STATUS _hdri_dl_status
)
list(GET _hdri_dl_status 0 _hdri_dl_code)
if(NOT _hdri_dl_code EQUAL 0)
file(REMOVE "${_hdri_dir}/${_hdri}")
message(WARNING "HDRI download failed for ${_hdri} (${_hdri_dl_status}) — the shader-ball lighting preset will fall back to a distant light.")
endif()
endif()
endforeach()
# Copy resources to build directory
file(COPY ${CMAKE_SOURCE_DIR}/resources
DESTINATION ${CMAKE_BINARY_DIR}
)
# Copy font and SVG icons to build directory
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:UsdLayerManager>/../resources/font"
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:UsdLayerManager>/resources/icons"
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_SOURCE_DIR}/third_party/Inter/Inter.ttc"
"$<TARGET_FILE_DIR:UsdLayerManager>/resources/font/Inter.ttc"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/resources/icons"
"$<TARGET_FILE_DIR:UsdLayerManager>/resources/icons"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CMAKE_SOURCE_DIR}/resources/hdri"
"$<TARGET_FILE_DIR:UsdLayerManager>/resources/hdri"
COMMENT "Copying fonts, SVG icons and HDRI presets to build output"
)
# Copy ACES OCIO config to build output — only on first build (skipped if already present).
if(EXISTS "${_ocio_config_dir}/config.ocio")
add_custom_command(TARGET UsdLayerManager POST_BUILD
COMMAND ${CMAKE_COMMAND}
"-DSRC=${_ocio_config_dir}"
"-DDST=$<TARGET_FILE_DIR:UsdLayerManager>/resources/OpenColorIO-Configs/aces_1.2"
-P "${CMAKE_SOURCE_DIR}/cmake/CopyDirIfMissing.cmake"
COMMENT "Copying ACES OCIO config to build output (first time only)"
)
endif()
# Install OpenColorIO DLL (from the OpenUSD distribution's bin/)
install(FILES
"${OpenUSD_ROOT_DIR}/bin/OpenColorIO_2_1.dll"
DESTINATION bin
OPTIONAL
)
# Install resources (icons, fonts placeholder) next to the exe so the
# exe-relative path "resources/..." resolves correctly from install/bin/.
install(DIRECTORY ${CMAKE_SOURCE_DIR}/resources
DESTINATION bin
)
# Install Inter font next to the exe under resources/font/
# ImGuiContext tries "resources/font/Inter.ttf" then "resources/font/Inter.ttc".
install(FILES ${CMAKE_SOURCE_DIR}/third_party/Inter/Inter.ttc
DESTINATION bin/resources/font
)
# Print configuration summary
message(STATUS "")
message(STATUS "=== USD Layer Manager Configuration ===")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "C++ standard: ${CMAKE_CXX_STANDARD}")
message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}")
message(STATUS "OpenUSD found: ${OpenUSD_FOUND}")
message(STATUS "ImGui found: ${Imgui_FOUND}")
message(STATUS "OpenGL found: ${OPENGL_FOUND}")
message(STATUS "Glad found: ${Glad_FOUND}")
message(STATUS "Build tests: ${BUILD_TESTS}")
message(STATUS "hdEmbree support: ${OPENUSD_HAS_EMBREE}")
if(OPENUSD_HAS_EMBREE AND EMBREE_DLLS)
message(STATUS "Embree DLLs: ${EMBREE_DLLS}")
elseif(OPENUSD_HAS_EMBREE)
message(WARNING "hdEmbree found but no Embree DLLs detected — set EMBREE_LOCATION to the Embree SDK bin/ dir or check HDEMBREE_USD_ROOT/bin")
endif()
message(STATUS "hdArnold support: ${OPENUSD_HAS_ARNOLD}")
if(OPENUSD_HAS_ARNOLD)
message(STATUS "hdArnold DLL: ${HDARNOLD_DLL}")
message(STATUS "Arnold runtime DLLs: ${ARNOLD_RUNTIME_DLLS}")
if(NOT ARNOLD_AI_DLL)
message(WARNING "hdArnold found but ai.dll not located — set ARNOLD_LOCATION to the MtoA root (e.g. C:/Autodesk/mtoa/5.5.0/2026)")
endif()
endif()
message(STATUS "hdCycles support: ${OPENUSD_HAS_CYCLES}")
if(OPENUSD_HAS_CYCLES)
message(STATUS "Cycles source: ${CMAKE_SOURCE_DIR}/third_party/cycles")
message(STATUS "Cycles build: ${CYCLES_BINARY_DIR}")
message(STATUS "Cycles install: ${CYCLES_INSTALL_DIR}")
endif()
message(STATUS "=======================================")
message(STATUS "")
# === Test build option ===
option(BUILD_TESTS "Build test executables" OFF)
if(BUILD_TESTS)
# === Viewport Display Test ===
add_executable(ViewportDisplayTest
tests/viewport_display_test.cpp
${CORE_SOURCES}
${UTILS_SOURCES}
)
target_include_directories(ViewportDisplayTest PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include
"${CMAKE_SOURCE_DIR}/third_party/ffmpeg/include"
)
target_link_libraries(ViewportDisplayTest PRIVATE
OpenUSD::OpenUSD
Glad::Glad
FFmpeg::FFmpeg
)
if(WIN32)
target_compile_definitions(ViewportDisplayTest PRIVATE
WIN32_LEAN_AND_MEAN
NOMINMAX
_CRT_SECURE_NO_WARNINGS
)
add_custom_command(TARGET ViewportDisplayTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_BIN_DIR}"
"$<TARGET_FILE_DIR:ViewportDisplayTest>"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/lib/usd"
"$<TARGET_FILE_DIR:ViewportDisplayTest>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/plugin/usd"
"$<TARGET_FILE_DIR:ViewportDisplayTest>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python312.dll"
"$<TARGET_FILE_DIR:ViewportDisplayTest>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python3.dll"
"$<TARGET_FILE_DIR:ViewportDisplayTest>"
COMMENT "Copying runtime DLLs and USD plugins for test..."
)
endif()
enable_testing()
add_test(NAME ViewportDisplayTest COMMAND ViewportDisplayTest)
add_executable(RendererDiagnosticTest
tests/renderer_diagnostic_test.cpp
${CORE_SOURCES}
${UTILS_SOURCES}
)
target_include_directories(RendererDiagnosticTest PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include
"${CMAKE_SOURCE_DIR}/third_party/ffmpeg/include"
)
target_link_libraries(RendererDiagnosticTest PRIVATE
OpenUSD::OpenUSD
Glad::Glad
FFmpeg::FFmpeg
)
if(WIN32)
target_compile_definitions(RendererDiagnosticTest PRIVATE
WIN32_LEAN_AND_MEAN
NOMINMAX
_CRT_SECURE_NO_WARNINGS
)
add_custom_command(TARGET RendererDiagnosticTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_BIN_DIR}"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/lib/usd"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${OpenUSD_ROOT_DIR}/plugin/usd"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>/usd"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python312.dll"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$ENV{LOCALAPPDATA}/Programs/Python/Python312/python3.dll"
"$<TARGET_FILE_DIR:RendererDiagnosticTest>"
COMMENT "Copying runtime DLLs and USD plugins for diagnostic test..."
)
endif()
endif() # BUILD_TESTS