Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
fi
JSON=$(jq -c . < "$MATRIX_FILE")
MODIFIED_JSON='{"include":[]}'
PLUGINS=("GLTF" "PLY" "OBJ" "STL" "FBX")
PLUGINS=("GLTF" "PLY" "SPZ" "OBJ" "STL" "FBX")

for row in $(echo "${JSON}" | jq -c '.include[]'); do
for plugin in "${PLUGINS[@]}"; do
Expand Down Expand Up @@ -195,6 +195,7 @@ jobs:
"-Dpxr_ROOT=${{ github.workspace }}/usd_build"
"-DUSD_FILEFORMATS_ENABLE_GLTF=$([[ "${{ matrix.config }}" == "ALL" || "${{ matrix.config }}" == "GLTF" ]] && echo "ON" || echo "OFF")"
"-DUSD_FILEFORMATS_ENABLE_PLY=$([[ "${{ matrix.config }}" == "ALL" || "${{ matrix.config }}" == "PLY" ]] && echo "ON" || echo "OFF")"
"-DUSD_FILEFORMATS_ENABLE_SPZ=$([[ "${{ matrix.config }}" == "ALL" || "${{ matrix.config }}" == "SPZ" ]] && echo "ON" || echo "OFF")"
"-DUSD_FILEFORMATS_ENABLE_OBJ=$([[ "${{ matrix.config }}" == "ALL" || "${{ matrix.config }}" == "OBJ" ]] && echo "ON" || echo "OFF")"
"-DUSD_FILEFORMATS_ENABLE_STL=$([[ "${{ matrix.config }}" == "ALL" || "${{ matrix.config }}" == "STL" ]] && echo "ON" || echo "OFF")"
"-DUSD_FILEFORMATS_ENABLE_FBX=$([[ "${{ matrix.config }}" == "ALL" || "${{ matrix.config }}" == "FBX" ]] && echo "ON" || echo "OFF")"
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ option(USD_FILEFORMATS_BUILD_TESTS "Build the unit tests" ON)
option(USD_FILEFORMATS_ENABLE_FBX "Enables fbx plugin" ON)
option(USD_FILEFORMATS_ENABLE_GLTF "Enables gltf plugin" ON)
option(USD_FILEFORMATS_ENABLE_OBJ "Enables obj plugin" ON)
option(USD_FILEFORMATS_ENABLE_SPZ "Enables spz plugin" ON)
option(USD_FILEFORMATS_ENABLE_PLY "Enables ply plugin" ON)
option(USD_FILEFORMATS_ENABLE_STL "Enables stl plugin" ON)
option(USD_FILEFORMATS_ENABLE_SBSAR "Enables sbsar plugin" OFF)
Expand All @@ -37,6 +38,7 @@ option(USD_FILEFORMATS_FETCH_DRACO "Forces FetchContent for Draco" OFF)
option(USD_FILEFORMATS_FETCH_ZLIB "Forces FetchContent for Zlib" OFF)
option(USD_FILEFORMATS_FETCH_LIBXML2 "Forces FetchContent for LibXml2" ON)
option(USD_FILEFORMATS_FETCH_HAPPLY "Forces FetchContent for Happly" ON)
option(USD_FILEFORMATS_FETCH_SPZ "Forces FetchContent for spz" ON)
option(USD_FILEFORMATS_FETCH_SPHERICAL_HARMONICS "Forces FetchContent for SphericalHarmonics" ON)
option(USD_FILEFORMATS_FETCH_FMT "Forces FetchContent for Fmt" ON)
option(USD_FILEFORMATS_FETCH_FASTFLOAT "Forces FetchContent for FastFLoat" ON)
Expand Down Expand Up @@ -110,6 +112,9 @@ endif()
if (USD_FILEFORMATS_ENABLE_SBSAR)
add_subdirectory(sbsar)
endif()
if (USD_FILEFORMATS_ENABLE_SPZ)
add_subdirectory(spz)
endif()
if (USD_FILEFORMATS_ENABLE_STL)
add_subdirectory(stl)
endif()
Expand Down
33 changes: 18 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ These [USD file-format-plugins](https://graphics.pixar.com/usd/release/plugins.h
| [usdobj](obj/README.md) | [Wavefront's obj](https://en.wikipedia.org/wiki/Wavefront_.obj_file) | `.obj` |
| [usdply](ply/README.md) | [Polygon File Format](https://en.wikipedia.org/wiki/PLY_(file_format)) | `.ply` |
| [usdsbsar](sbsar/README.md) | [SBSAR file format](https://developer.adobe.com/console/servicesandapis#) | `.sbsar` |
| [usdspz](spz/README.md) | [Niantic Labs SPZ](https://scaniverse.com/news/spz-gaussian-splat-open-source-file-format) | `.spz` |
| [usdstl](stl/README.md) | [STL file format](https://en.wikipedia.org/wiki/STL_(file_format)) | `.stl` |


Expand All @@ -41,19 +42,20 @@ The following tools are needed:
The following dependencies are needed:
|Dependency|Version|Affects|Optional|
|--|--|--|--|
| [Pixar USD](https://github.com/PixarAnimationStudios/USD) | 23.08 | all | no |
| [GTest](https://github.com/google/googletest.git) | 1.11.0 | all tests | yes |
| [Eigen](https://gitlab.com/libeigen/eigen) | 3.4.0 | usdply | no |
| [FBX SDK](https://aps.autodesk.com/developer/overview/fbx-sdk) | 2020.3.7 | usdfbx | no |
| [LibXml2](https://gitlab.gnome.org/GNOME/libxml2) | 2.10.0 | usdfbx | no |
| [Zlib](https://github.com/madler/zlib.git) | 1.2.11 | usdfbx | no |
| [TinyGltf](https://github.com/syoyo/tinygltf) | 2.8.21 | usdgltf | no |
| [Draco](https://github.com/google/draco.git) | 1.56 | usdgltf | yes |
| [Fmt](https://github.com/fmtlib/fmt.git) | 10.1.1 | usdobj | no |
| [FastFloat](https://github.com/lemire/fast_float.git) | 1.1.2 | usdobj | no |
| [Happly](https://github.com/nmwsharp/happly.git) | cfa2611 | usdply | no |
| [Spherical Harmonics](https://github.com/google/spherical-harmonics) | ccb6c7f | usdply | no |
| [Substance](https://developer.adobe.com/substance3d-sdk/) | 9.1.2 | usdsbsar | no |
| [Pixar USD](https://github.com/PixarAnimationStudios/USD) | 23.08 | all | no |
| [GTest](https://github.com/google/googletest.git) | 1.11.0 | all tests | yes |
| [Eigen](https://gitlab.com/libeigen/eigen) | 3.4.0 | usdply, usdspz | no |
| [FBX SDK](https://aps.autodesk.com/developer/overview/fbx-sdk) | 2020.3.7 | usdfbx | no |
| [LibXml2](https://gitlab.gnome.org/GNOME/libxml2) | 2.10.0 | usdfbx | no |
| [Zlib](https://github.com/madler/zlib.git) | 1.2.11 | usdfbx, usdgltf | no |
| [TinyGltf](https://github.com/syoyo/tinygltf) | 2.8.21 | usdgltf | no |
| [Draco](https://github.com/google/draco.git) | 1.56 | usdgltf | yes |
| [Fmt](https://github.com/fmtlib/fmt.git) | 10.1.1 | usdobj | no |
| [FastFloat](https://github.com/lemire/fast_float.git) | 1.1.2 | usdobj | no |
| [Happly](https://github.com/nmwsharp/happly.git) | cfa2611 | usdply | no |
| [Spherical Harmonics](https://github.com/google/spherical-harmonics) | ccb6c7f | usdply, usdspz | no |
| [Spz](https://github.com/nianticlabs/spz) | fd4e2a5 | usdspz | no |
| [Substance](https://developer.adobe.com/substance3d-sdk/) | 9.1.2 | usdsbsar | no |

## Build

Expand Down Expand Up @@ -130,8 +132,9 @@ where:
| -DUSD_FILEFORMATS_ENABLE_GLTF | Enables gltf plugin | ON | usdgltf |
| -DUSD_FILEFORMATS_ENABLE_OBJ | Enables obj plugin | ON | usdobj |
| -DUSD_FILEFORMATS_ENABLE_PLY | Enables ply plugin | ON | usdply |
| -DUSD_FILEFORMATS_ENABLE_STL | Enables stl plugin | ON | usdstl |
| -DUSD_FILEFORMATS_ENABLE_SBSAR | Enables sbsar plugin | OFF | usdsbsar |
| -DUSD_FILEFORMATS_ENABLE_SPZ | Enables spz plugin | ON | usdspz |
| -DUSD_FILEFORMATS_ENABLE_STL | Enables stl plugin | ON | usdstl |
| -DUSD_FILEFORMATS_ENABLE_SBSAR | Enables sbsar plugin | OFF | usdsbsar |
| -DUSD_FILEFORMATS_ENABLE_DRACO | Enables draco in usdgltf | OFF | usdgltf |
| -DUSD_FILEFORMATS_FORCE_FETCHCONTENT | Forces FetchContent for various packages | OFF | all |
| -DUSD_FILEFORMATS_FETCH_GTEST | Forces FetchContent for GTest | ON | all tests |
Expand Down
25 changes: 25 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
v1.1.1 March 10th, 2025
fbx:
- added null and index checks
- properly write images when import images arg is invoked
- add support for invisibility
gltf:
- export normal scale
- ignore invisible nodes on export
obj:
- fix parser
- ignore invisible nodes on export
ply:
- ignore invisible nodes on export
sbsar:
- fix for isImageFileSupported() [fixes nested sbsarimages]
spz:
- ignore invisible nodes on export
- initial
stl:
- ignore invisible nodes on export
utility:
- don't create subdivisionRule attribute with value none
- fix for isImageFileSupported() [fixes nested usdz images]
- prevent bad access in utils

v1.1.0 January 31st, 2025
fbx:
- add display name to USD to save imported names for export
Expand Down
107 changes: 107 additions & 0 deletions cmake/Findspz.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#[=======================================================================[.rst:
----

Finds or fetches the spz library.
If USD_FILEFORMATS_FORCE_FETCHCONTENT or USD_FILEFORMATS_FETCH_SPZ are
TRUE, spz will be fetched. Otherwise it will be searched via find commands.

Imported Targets
^^^^^^^^^^^^^^^^

This module provides the following imported targets, if fetched:

``spz::spz``
The spz library

Result Variables
^^^^^^^^^^^^^^^^

This will define the following variables:

``spz_FOUND``

Cache Variables
^^^^^^^^^^^^^^^

The following cache variables may also be set:

``SPZ_INCLUDE_DIR``
The directory containing ``splat-types.h``.

#]=======================================================================]

if(TARGET spz::spz)
return()
endif()

if(NOT TARGET ZLIB::ZLIB)
find_package(ZLIB REQUIRED)
endif()

if(USD_FILEFORMATS_FORCE_FETCHCONTENT OR USD_FILEFORMATS_FETCH_SPZ)
message(STATUS "Fetching spz")
include(FetchContent)
FetchContent_Declare(
spz
GIT_REPOSITORY "https://github.com/raymondyfei/spz.git"
GIT_TAG "fd4e2a57bd6b7462657d41eebda330eca0f35159"
OVERRIDE_FIND_PACKAGE
)
FetchContent_MakeAvailable(spz)
if (spz_POPULATED)
set(spz_FOUND TRUE)
file(GLOB SPZ_SRC_FILES
${spz_SOURCE_DIR}/src/cc/*.cc
${spz_SOURCE_DIR}/src/cc/*.h
)
add_library(spz STATIC)
target_sources(spz PRIVATE ${SPZ_SRC_FILES})
set(SPZ_INCLUDE_DIR "${spz_SOURCE_DIR}/src/cc")
target_include_directories(spz PUBLIC ${SPZ_INCLUDE_DIR})
target_link_libraries(spz PRIVATE ZLIB::ZLIB)
set_property(TARGET spz PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET spz PROPERTY CXX_STANDARD 17)
target_compile_definitions(spz PRIVATE "_USE_MATH_DEFINES")
if (NOT MSVC)
target_compile_options(spz PRIVATE "-Wno-shorten-64-to-32")
endif()

add_library(spz::spz ALIAS spz)
elseif(${spz_FIND_REQUIRED})
message(FATAL_ERROR "Could not fetch spz")
endif()
else()
include(FindPackageHandleStandardArgs)

find_path(SPZ_INCLUDE_DIR
NAMES splat-types.h
)

find_package_handle_standard_args(spz
REQUIRED_VARS SPZ_INCLUDE_DIR
)

if(spz_FOUND)
file(GLOB SPZ_SRC_FILES
${SPZ_INCLUDE_DIR}/*.cc
${SPZ_INCLUDE_DIR}/*.h
)
add_library(spz STATIC)
target_sources(spz PRIVATE ${SPZ_SRC_FILES})
target_include_directories(spz PUBLIC ${SPZ_INCLUDE_DIR})
target_link_libraries(spz PRIVATE ZLIB::ZLIB)
set_property(TARGET spz PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET spz PROPERTY CXX_STANDARD 17)
target_compile_definitions(spz PRIVATE "_USE_MATH_DEFINES")

if (NOT MSVC)
target_compile_options(spz PRIVATE "-Wno-shorten-64-to-32")
endif()

add_library(spz::spz ALIAS spz)
elseif(${spz_FIND_REQUIRED})
message(FATAL_ERROR "Could not find spz")
endif()
endif()


4 changes: 2 additions & 2 deletions fbx/src/fbx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ EmbedReadCBFunction(void* pUserData,
}

bool
readFbx(Fbx& fbx, const std::string& filename, bool onlyMaterials)
readFbx(Fbx& fbx, const std::string& filename, bool importImages, bool onlyMaterials)
{
GUARD(fbx.manager != nullptr, "Invalid fbx manager");

Expand All @@ -228,7 +228,7 @@ readFbx(Fbx& fbx, const std::string& filename, bool onlyMaterials)
ios->SetBoolProp(IMP_FBX_TEXTURE, true);
ios->SetBoolProp(IMP_FBX_ANIMATION, !onlyMaterials);
ios->SetBoolProp(IMP_FBX_MODEL, !onlyMaterials);
fbx.loadImages = onlyMaterials;
fbx.loadImages = importImages;

if (!importer->Initialize(filename.c_str(), -1, ios)) {
FbxString error = importer->GetStatus().GetErrorString();
Expand Down
9 changes: 8 additions & 1 deletion fbx/src/fbx.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,15 @@ struct Fbx
~Fbx();
};

/*
* importImages Indicates whether the fbx should be set to load image data. It should be true if
* the images are being written out, and false otherwise
*
* onlyMaterials Indicates whether the fbx should only load materials. It should only be true if
* the file is being loaded just to separately load image textures, and nothing else is being used
*/
bool
readFbx(Fbx& fbx, const std::string& filename, bool onlyMaterials);
readFbx(Fbx& fbx, const std::string& filename, bool importImages, bool onlyMaterials);

bool
writeFbx(const ExportFbxOptions& options, const Fbx& fbx, const std::string& filename);
Expand Down
26 changes: 22 additions & 4 deletions fbx/src/fbxExport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1295,13 +1295,23 @@ exportFbxNodes(ExportFbxContext& ctx)
parent->AddChild(fbxNode);
exportFbxTransform(ctx, node, fbxNode);

if (node.markedInvisible) {
fbxNode->SetVisibility(false);
}
if (node.camera >= 0) {
// Ignore camera invisibility, since it isn't important enough to add a new node
FbxCamera* fbxCamera = ctx.cameras[node.camera];
fbxNode->AddNodeAttribute(fbxCamera);
}
if (node.light >= 0) {
FbxLight* fbxLight = ctx.lights[node.light];
fbxNode->AddNodeAttribute(fbxLight);
FbxNode* container = fbxNode;
if (ctx.usd->lights[node.light].markedInvisible) {
container = FbxNode::Create(ctx.fbx->scene, "light_visibility");
container->SetVisibility(false);
fbxNode->AddChild(container);
}
container->AddNodeAttribute(fbxLight);
}

for (const auto& [skeletonIndex, meshIndices] : node.skinnedMeshes) {
Expand Down Expand Up @@ -1334,10 +1344,18 @@ exportFbxNodes(ExportFbxContext& ctx)
}
const Mesh& m = ctx.usd->meshes[meshIndex];
FbxNode* container = fbxNode;
if (node.staticMeshes.size() > 1) {

std::string containerName = getNodeName(node).c_str() + std::to_string(i);
if (node.staticMeshes.size() > 1 || m.markedInvisible) {
// Name the node based on the child index, unless there is only one child, in
// which case the node is only present to preserve visibility
std::string containerName =
node.staticMeshes.size() > 1
? getNodeName(node).c_str() + std::to_string(i)
: getNodeName(node).c_str() + std::string("_visibility");
container = FbxNode::Create(ctx.fbx->scene, containerName.c_str());

if (m.markedInvisible) {
container->SetVisibility(false);
}
fbxNode->AddChild(container);
}
FbxMesh* fbxMesh = ctx.meshes[meshIndex];
Expand Down
49 changes: 46 additions & 3 deletions fbx/src/fbxImport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,15 @@ importFbxMesh(ImportFbxContext& ctx, FbxMesh* fbxMesh, int parent)
if (clusterCount > 0) {
isSkinnedMesh = true;
FbxCluster* firstCluster = skin->GetCluster(0);
if (firstCluster == nullptr) {
TF_WARN("Skin: %d does not have a first cluster.\n", i);
continue;
}
FbxNode* firstlink = firstCluster->GetLink();
if (firstlink == nullptr) {
TF_WARN("Skin: %d first cluster does not have a first link.\n", i);
continue;
}
size_t skeletonIndex = ctx.skeletonsMap[firstlink];

ctx.meshSkinsMap[meshIndex] = skeletonIndex;
Expand All @@ -503,7 +511,15 @@ importFbxMesh(ImportFbxContext& ctx, FbxMesh* fbxMesh, int parent)

for (int j = 0; j < clusterCount; j++) {
FbxCluster* cluster = skin->GetCluster(j);
if (cluster == nullptr) {
TF_WARN("No cluster at skin index %d.\n", j);
continue;
}
FbxNode* link = cluster->GetLink();
if (link == nullptr) {
TF_WARN("No link at skin index %d.\n", j);
continue;
}

size_t jointIndex = ctx.bonesMap[link];

Expand All @@ -527,11 +543,28 @@ importFbxMesh(ImportFbxContext& ctx, FbxMesh* fbxMesh, int parent)
int clusterControlPointIndicesCount = cluster->GetControlPointIndicesCount();
int* clusterControlPointIndices = cluster->GetControlPointIndices();
double* pointsWeights = cluster->GetControlPointWeights();
if (clusterControlPointIndices == nullptr) {
TF_WARN("No cluster control point indices for skin cluster: %d.\n", j);
continue;
}
if (pointsWeights == nullptr) {
TF_WARN("No point weights for skin cluster: %d.\n", j);
continue;
}
for (int k = 0; k < clusterControlPointIndicesCount; k++) {
int controlPointIndex = clusterControlPointIndices[k];
double influenceWeight = pointsWeights[k];
indexes[controlPointIndex].push_back(jointIndex);
weights[controlPointIndex].push_back(influenceWeight);
if (controlPointIndex > indexes.size() || controlPointIndex > weights.size()) {
TF_WARN("Control Point Index outside of index or weight bounds. index: %d "
" Index Size: %d Weight Size: %d",
controlPointIndex,
indexes.size(),
weights.size());
continue;
} else {
double influenceWeight = pointsWeights[k];
indexes[controlPointIndex].push_back(jointIndex);
weights[controlPointIndex].push_back(influenceWeight);
}
}
}
}
Expand Down Expand Up @@ -2001,6 +2034,16 @@ importFbxNodes(ImportFbxContext& ctx, FbxNode* fbxNode, int parent)
auto [nodeIndex, node] = ctx.usd->addNode(parent);
node.name = fbxNode->GetName();

if (!fbxNode->GetVisibility()) {
node.markedInvisible = true;
}
if (!fbxNode->VisibilityInheritance.Get()) { // True by default
TF_WARN("importFbxNodes: Node %s does not inherit visibility (VisibilityInheritance = "
"false). This is currently unsupported. The node is set as %s",
fbxNode->GetName(),
node.markedInvisible ? "invisible" : "visible");
}

ctx.nodeMap[fbxNode] = nodeIndex;

TF_DEBUG_MSG(FILE_FORMAT_FBX, "importFbx: node %s\n", node.name.c_str());
Expand Down
Loading