From 91a7bbd7a00e15dca898ff4bb190c63a100191cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Delgado=20Kr=C3=A4mer?= Date: Sun, 27 Oct 2024 14:48:36 +0100 Subject: [PATCH] WIP --- CMakeLists.txt | 12 +- src/libguc/CMakeLists.txt | 11 ++ src/libguc/src/cgltf_util.cpp | 292 +++++++++++++++++++++++++++++++++- src/libguc/src/image.cpp | 2 +- src/libguc/src/materialx.cpp | 1 - 5 files changed, 310 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 749eb27..feb3ef6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,13 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR) - +#set(CMAKE_FIND_DEBUG_MODE 1) project(guc VERSION 0.5) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_MODULE_PATH "${CMAKE_PROJECT_DIR}/cmake") + # Find USD - only non-monolithic builds have been tested. find_package(pxr CONFIG REQUIRED) @@ -14,9 +16,15 @@ find_package(pxr CONFIG REQUIRED) find_package(MaterialX 1.38.6 REQUIRED HINTS ${pxr_DIR}) # We need to open PNG and JPEG files in order to read the number of channels -# for shading node creation. OIIO should be provided by the USD installation. +# for shading node creation. OIIO can be provided by the USD installation, +# otherwise we fall back to stb_image. find_package(OpenImageIO HINTS ${pxr_DIR}) +# Draco CMake config file is broken in this version, rely on Find* script. +# TODO: set Draco_DIR / ROOT instead of pxr_DIR +#find_package(XDraco)# HINTS ${pxr_DIR}) +include(cmake/FindXDraco.cmake) + option(GUC_BUILD_EXECUTABLE "Build the guc executable." ON) option(GUC_BUILD_USDGLTF "Build the Sdf file format plugin." OFF) diff --git a/src/libguc/CMakeLists.txt b/src/libguc/CMakeLists.txt index 850c565..2c06abe 100644 --- a/src/libguc/CMakeLists.txt +++ b/src/libguc/CMakeLists.txt @@ -62,6 +62,16 @@ else() list(APPEND LIBGUC_SHARED_LIBRARIES $) endif() +if(draco_FOUND) + list(APPEND LIBGUC_DEFINES "GUC_USE_DRACO") + if(NOT TARGET usd_ms) + list(APPEND LIBGUC_SHARED_LIBRARIES ${DRACO_LIBRARIES}) + endif() +# TODO: how are headers included? --> need target +else() + message(FATAL_ERROR "Draco NOT FOUND :(") +endif() + # # libguc # @@ -77,6 +87,7 @@ target_link_libraries(libguc PRIVATE ${LIBGUC_SHARED_LIBRARIES}) target_include_directories(libguc PUBLIC $ PUBLIC $ +${DRACO_INCLUDE_DIRS} ) set_target_properties( diff --git a/src/libguc/src/cgltf_util.cpp b/src/libguc/src/cgltf_util.cpp index c05f1f8..8a6164d 100644 --- a/src/libguc/src/cgltf_util.cpp +++ b/src/libguc/src/cgltf_util.cpp @@ -27,9 +27,16 @@ #include -#include +#ifdef GUC_USE_DRACO +#include +#include +#endif + #include +#include #include +#include +#include #include "debugCodes.h" @@ -38,6 +45,7 @@ using namespace PXR_NS; namespace detail { constexpr static const char* GLTF_EXT_MESHOPT_COMPRESSION_EXTENSION_NAME = "EXT_meshopt_compression"; + constexpr static const char* GLTF_KHR_DRACO_MESH_COMPRESSION_EXTENSION_NAME = "KHR_draco_mesh_compression"; bool extensionSupported(const char* name) { @@ -55,7 +63,8 @@ namespace detail strcmp(name, "KHR_materials_volume") == 0 || strcmp(name, "KHR_mesh_quantization") == 0 || strcmp(name, "KHR_texture_transform") == 0 || - strcmp(name, GLTF_EXT_MESHOPT_COMPRESSION_EXTENSION_NAME) == 0; + strcmp(name, GLTF_EXT_MESHOPT_COMPRESSION_EXTENSION_NAME) == 0 || + strcmp(name, GLTF_KHR_DRACO_MESH_COMPRESSION_EXTENSION_NAME) == 0; } struct BufferHolder @@ -118,6 +127,258 @@ namespace detail bufferHolder->map.erase(bufferPtr); } +#ifdef GUC_USE_DRACO + template + bool convertDracoVertexAttributes(const draco::PointAttribute* attribute, + size_t vertexCount, + uint8_t* result) + { + uint32_t elemSize = sizeof(T) * attribute->num_components(); + + T elems[4/*max components*/]; + for (size_t i = 0; i < vertexCount; i++) + { + draco::PointIndex pointIndex(i); + draco::AttributeValueIndex valueIndex = attribute->mapped_index(pointIndex); + + if (!attribute->ConvertValue(valueIndex, attribute->num_components(), elems)) + { + return false; + } + + uint32_t elemOffset = i * elemSize; + memcpy(&result[elemOffset], &elems[0], elemSize); + } + return true; + } + + bool convertDracoVertexAttributes(cgltf_component_type componentType, + const draco::PointAttribute* attribute, + size_t vertexCount, + uint8_t* result) + { + bool success = false; + switch (componentType) + { + case cgltf_component_type_r_8: + success = convertDracoVertexAttributes(attribute, vertexCount, result); + case cgltf_component_type_r_8u: + success = convertDracoVertexAttributes(attribute, vertexCount, result); + case cgltf_component_type_r_16: + success = convertDracoVertexAttributes(attribute, vertexCount, result); + case cgltf_component_type_r_16u: + success = convertDracoVertexAttributes(attribute, vertexCount, result); + case cgltf_component_type_r_32u: + success = convertDracoVertexAttributes(attribute, vertexCount, result); + case cgltf_component_type_r_32f: + success = convertDracoVertexAttributes(attribute, vertexCount, result); + break; + default: + break; + } + return success; + } + + bool decompressDraco(cgltf_data* data) + { + // TODO: rename Accessor -> Attribute + + // + // Phase 1: TODO + // + // TODO: rename since not only type + struct DenseAccessorComponentType + { + cgltf_accessor* srcAccessor; + int dracoUid; + const cgltf_accessor* dracoAccessor; + size_t stride; // TODO: can just use srcAccessor.stride instead + size_t size; + }; + using DenseAccessorComponentTypes = std::vector; + + std::map accessorsToDecompress; + + for (size_t i = 0; i < data->meshes_count; ++i) + { + const cgltf_mesh& mesh = data->meshes[i]; + + for (size_t j = 0; j < mesh.primitives_count; j++) + { + const cgltf_primitive& primitive = mesh.primitives[j]; + + if (!primitive.has_draco_mesh_compression) + { + continue; + } + + const cgltf_draco_mesh_compression* draco = &primitive.draco_mesh_compression; + + if (accessorsToDecompress.count(draco) == 0) + { + accessorsToDecompress[draco] = {}; + } + + DenseAccessorComponentTypes& mapEnty = accessorsToDecompress[draco]; + +// TODO: "The attributes defined in the extension must be a subset of the attributes of the primitive." + for (size_t i = 0; i < draco->attributes_count; i++) + { + const cgltf_attribute* dracoAttr = &draco->attributes[i]; + cgltf_attribute* gltfAttr = nullptr; + + for (size_t j = 0; j < primitive.attributes_count; j++) + { + cgltf_attribute* newAttr = &primitive.attributes[i]; + + if (strcmp(newAttr->name, dracoAttr->name) == 0) + { + gltfAttr = newAttr; + break; + } + } + + if (!gltfAttr || !gltfAttr->data) + { + TF_RUNTIME_ERROR("TODO - Illegal use of draco extension - TODO"); + return false; + } + + cgltf_accessor* accessor = gltfAttr->data; + + size_t stride = accessor->stride;//cgltf_calc_size(accessor->type, accessor->component_type); + size_t size = stride * accessor->count; + + auto dracoUid = int(cgltf_accessor_index(data, dracoAttr->data)); + mapEnty.push_back({ accessor, dracoUid, dracoAttr->data, stride, size }); + } + + primitive.indices->offset = 0; + primitive.indices->stride = sizeof(uint32_t); + primitive.indices->buffer_view = draco->buffer_view; + } + } + + // + // Phase 2: TODO + // + for (auto it = accessorsToDecompress.begin(); it != accessorsToDecompress.end(); ++it) + { + // Decompress into mesh + const cgltf_draco_mesh_compression* draco = it->first; + cgltf_buffer_view* bufferView = draco->buffer_view; // TODO: ref? + cgltf_buffer* buffer = bufferView->buffer; + + const char* bufferData = ((const char*) buffer->data) + bufferView->offset; + + draco::DecoderBuffer decoderBuffer; + decoderBuffer.Init(bufferData, bufferView->size); + + auto geomType = draco::Decoder::GetEncodedGeometryType(&decoderBuffer); + if (!geomType.ok() || geomType.value() != draco::TRIANGULAR_MESH) + { + return false; + } + + draco::Decoder decoder; + auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer); + if (!decodeResult.ok()) + { + return false; + } + + const std::unique_ptr& mesh = decodeResult.value(); + + // Allocate decoded buffer + const DenseAccessorComponentTypes& attrsToDecode = it->second; + + uint32_t faceCount = mesh->num_faces(); + uint32_t indexCount = faceCount * 3; + + uint32_t indicesSize = indexCount * sizeof(uint32_t); + + size_t attributesSize = 0; + for (const DenseAccessorComponentType& c : attrsToDecode) + { + attributesSize += mesh->num_points() * c.stride; + } + + bufferView->data = malloc(indicesSize + attributesSize); + + // Write decoded data + auto baseIndex = draco::FaceIndex(0); + TF_VERIFY(sizeof(mesh->face(baseIndex)[0]) == 4); + memcpy(bufferView->data, &mesh->face(baseIndex)[0], indicesSize); + + size_t attributeOffset = indicesSize; + for (const DenseAccessorComponentType& c : attrsToDecode) + { + const draco::PointAttribute* dracoAttr = mesh->GetAttributeByUniqueId(c.dracoUid); +//fprintf(stderr,"%u\n",uint32_t(c.dracoUid)); + +// TODO: something seems wrong with this?.. both primvars have the same value. why? +assert(c.srcAccessor->count == mesh->num_points()); + if (!convertDracoVertexAttributes(c.srcAccessor->component_type, + dracoAttr, + mesh->num_points(), + ((uint8_t*) bufferView->data) + attributeOffset)) + { + TF_WARN("FAILED DRACO VERTEX DATA CONVERSION"); // TODO: msg + return false; + } + + cgltf_accessor* srcAccessor = c.srcAccessor; + srcAccessor->buffer_view = draco->buffer_view; + srcAccessor->offset = attributeOffset; + + attributeOffset += mesh->num_points() * c.stride; // TODO: need the new stride here + } + } + + // + // Phase 3: Write-back TODO: text + // +/* + for (size_t i = 0; i < data->meshes_count; ++i) + { + const cgltf_mesh& mesh = data->meshes[i]; + + for (size_t j = 0; j < mesh.primitives_count; j++) + { + const cgltf_primitive& primitive = mesh.primitives[j]; + + if (!primitive.has_draco_mesh_compression) + { + continue; + } + + const cgltf_draco_mesh_compression* draco = &primitive.draco_mesh_compression; + + primitive.indices->offset = 0; + primitive.indices->stride = sizeof(uint32_t); + primitive.indices->buffer_view = draco->buffer_view; + + const DenseAccessorComponentTypes& mapEnty = accessorsToDecompress[draco]; + + size_t indicesSize = primitive.indices.count * primitive.indices.stride; + + size_t attributeOffset = indicesSize; + for (const DenseAccessorComponentType& c : mapEntry) + { + + + + attributeOffset += size; + } + + } + } +*/ + + return true; + } +#endif + // Based on https://github.com/jkuhlmann/cgltf/pull/129 cgltf_result decompressMeshopt(cgltf_data* data) { @@ -241,6 +502,8 @@ namespace guc } bool meshoptCompressionRequired = false; + [[maybe_unused]] + bool dracoMeshCompressionRequired = false; for (size_t i = 0; i < (*data)->extensions_required_count; i++) { @@ -252,6 +515,11 @@ namespace guc meshoptCompressionRequired = true; } + if (strcmp(ext, detail::GLTF_KHR_DRACO_MESH_COMPRESSION_EXTENSION_NAME) == 0) + { + dracoMeshCompressionRequired = true; + } + if (detail::extensionSupported(ext)) { continue; @@ -278,6 +546,22 @@ namespace guc TF_WARN(errStr, guc::cgltf_error_string(result)); } +#ifdef GUC_USE_DRACO + if (!detail::decompressDraco(*data)) + { + const char* errStr = "unable to decode Draco data"; + + if (dracoMeshCompressionRequired) + { + TF_RUNTIME_ERROR("%s", errStr); + free_gltf(*data); + return false; + } + + TF_WARN("%s", errStr); + } +#endif + for (size_t i = 0; i < (*data)->extensions_used_count; i++) { const char* ext = (*data)->extensions_used[i]; @@ -303,8 +587,8 @@ namespace guc const char* cgltf_error_string(cgltf_result result) { - assert(result != cgltf_result_success); - assert(result != cgltf_result_invalid_options); + TF_VERIFY(result != cgltf_result_success); + TF_VERIFY(result != cgltf_result_invalid_options); switch (result) { case cgltf_result_legacy_gltf: diff --git a/src/libguc/src/image.cpp b/src/libguc/src/image.cpp index 1b6d1d7..af41cb3 100644 --- a/src/libguc/src/image.cpp +++ b/src/libguc/src/image.cpp @@ -207,7 +207,7 @@ namespace detail auto image = OIIO::ImageInput::open(path, nullptr, &memReader); if (image) { - assert(image->supports("ioproxy")); + TF_VERIFY(image->supports("ioproxy")); const OIIO::ImageSpec& spec = image->spec(); channelCount = spec.nchannels; diff --git a/src/libguc/src/materialx.cpp b/src/libguc/src/materialx.cpp index f1ec31a..d93e931 100644 --- a/src/libguc/src/materialx.cpp +++ b/src/libguc/src/materialx.cpp @@ -25,7 +25,6 @@ #include #include -#include #include "naming.h" #include "debugCodes.h"