Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pablode committed Nov 4, 2024
1 parent 7005895 commit 4ef66a3
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 3 deletions.
10 changes: 9 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ 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)

Expand All @@ -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)

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ An example asset conversion is described in the [Structure Mapping](docs/Structu
Name | Status                        
------------------------------------|----------
EXT_meshopt_compression | ✅ Complete
KHR_draco_mesh_compression | ✅ Complete
KHR_lights_punctual | ✅ Partial <sup>1</sup>
KHR_materials_clearcoat | ✅ Complete
KHR_materials_emissive_strength | ✅ Complete
Expand Down
40 changes: 40 additions & 0 deletions cmake/FindXDraco.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# TODO
# draco 1.3.0(or 6?) CMake config file is broken.
# fixed in latest version, but not used by USD
#
# TODO: pay attention to casing (draco != Draco)
# we use this to prefer this Find file over the broken config
#
# USD does the same, but the authored config file is broken
# in two ways. First, patterns of the form 'libdraco.1.dylib'
# on macOS are not covered. Second, it does not find and provide
# the dracodec library which we need to link against.

# USD's FindDraco.cmake / config files do not cover the macOS
# 'libdraco.1.dylib' case, so we roll our own Find* file.

unset(draco_FOUND)
unset(draco_INCLUDE_DIRS)
unset(draco_LIBRARIES)

mark_as_advanced(draco_FOUND)
mark_as_advanced(draco_INCLUDE_DIRS)
mark_as_advanced(draco_LIBRARIES)

set(draco_version_file_no_prefix "draco/src/draco/core/draco_version.h")
find_path(draco_INCLUDE_DIRS NAMES "${draco_version_file_no_prefix}")

find_library(DRACO_LIBRARY NAMES draco PATHS "${pxr_DIR}/lib")
find_library(DRACO_DEC_LIBRARY NAMES dracodec PATHS "${pxr_DIR}/lib")
set(DRACO_LIBRARIES ${DRACO_LIBRARY} ${DRACO_DEC_LIBRARY})

message(STATUS ${draco_INCLUDE_DIRS})
message(STATUS ${DRACO_LIBRARY})
message(STATUS ${DRACO_DEC_LIBRARY})

if(draco_INCLUDE_DIRS AND DRACO_LIBRARY AND DRACO_DEC_LIBRARY)
set(draco_FOUND YES)
endif()

set(draco_FOUND YES)
set(XDraco_FOUND YES)
11 changes: 11 additions & 0 deletions src/libguc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ else()
list(APPEND LIBGUC_SHARED_LIBRARIES $<BUILD_INTERFACE:stb_image>)
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
#
Expand All @@ -77,6 +87,7 @@ target_link_libraries(libguc PRIVATE ${LIBGUC_SHARED_LIBRARIES})
target_include_directories(libguc
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PUBLIC $<INSTALL_INTERFACE:include>
${DRACO_INCLUDE_DIRS}
)

set_target_properties(
Expand Down
248 changes: 246 additions & 2 deletions src/libguc/src/cgltf_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@

#include <meshoptimizer.h>

#include <assert.h>
#ifdef GUC_USE_DRACO
#include <draco/compression/decode.h>
#include <draco/core/decoder_buffer.h>
#endif

#include <string.h>
#include <unordered_map>

Expand All @@ -38,6 +42,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)
{
Expand All @@ -55,7 +60,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
Expand Down Expand Up @@ -118,6 +124,221 @@ namespace detail
bufferHolder->map.erase(bufferPtr);
}

#ifdef GUC_USE_DRACO
template<typename T>
bool convertDracoVertexAttributes(const draco::PointAttribute* attribute,
size_t vertexCount,
uint8_t* result)
{
uint32_t elemSize = sizeof(T) * attribute->num_components();

const static uint32_t MAX_COMPONENT_COUNT = 4;

T elems[MAX_COMPONENT_COUNT];
for (size_t i = 0; i < vertexCount; i++)
{
draco::PointIndex pointIndex(i);
draco::AttributeValueIndex valueIndex = attribute->mapped_index(pointIndex);

if (!attribute->ConvertValue<T>(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<int8_t>(attribute, vertexCount, result);
case cgltf_component_type_r_8u:
success = convertDracoVertexAttributes<uint8_t>(attribute, vertexCount, result);
case cgltf_component_type_r_16:
success = convertDracoVertexAttributes<int16_t>(attribute, vertexCount, result);
case cgltf_component_type_r_16u:
success = convertDracoVertexAttributes<uint16_t>(attribute, vertexCount, result);
case cgltf_component_type_r_32u:
success = convertDracoVertexAttributes<uint32_t>(attribute, vertexCount, result);
case cgltf_component_type_r_32f:
success = convertDracoVertexAttributes<float>(attribute, vertexCount, result);
break;
default:
break;
}
return success;
}

bool decompressDraco(cgltf_data* data)
{
// TODO: rename Accessor -> Attribute
// TODO: further var rename + cleanup

//
// Phase 1: TODO description
//
struct DenseAccessorComponentType // TODO: rename since not only type
{
cgltf_accessor* srcAccessor;
int dracoUid;
};
using DenseAccessorComponentTypes = std::vector<DenseAccessorComponentType>;

std::map<const cgltf_draco_mesh_compression*, DenseAccessorComponentTypes> accessorsToDecompress; // TODO: rename?

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) // TODO: spec violation? if so, leave comment
{
TF_RUNTIME_ERROR("Draco attributes do not match source attributes");
return false;
}

auto dracoUid = int(cgltf_accessor_index(data, dracoAttr->data));
mapEnty.push_back({ gltfAttr->data, dracoUid });
}

primitive.indices->offset = 0;
primitive.indices->stride = sizeof(uint32_t);
primitive.indices->buffer_view = draco->buffer_view;
}
}

//
// Phase 2: TODO description (explain constraints and trick)
//
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: can this be null?
cgltf_buffer* buffer = bufferView->buffer; // TODO: can this be null?

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)
{
TF_RUNTIME_ERROR("unsupported Draco geometry type");
return false;
}

draco::Decoder decoder;
auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
if (!decodeResult.ok())
{
TF_RUNTIME_ERROR("Draco failed to decode mesh from buffer");
return false;
}

const std::unique_ptr<draco::Mesh>& mesh = decodeResult.value();

// Allocate decoded buffer
const DenseAccessorComponentTypes& attrsToDecode = it->second;

uint32_t vertexCount = mesh->num_points();
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)
{
cgltf_accessor* srcAccessor = c.srcAccessor;

attributesSize += vertexCount * srcAccessor->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);
if (!dracoAttr)
{
TF_RUNTIME_ERROR("invalid Draco attribute Uid");
return false;
}

cgltf_accessor* srcAccessor = c.srcAccessor;

TF_VERIFY(srcAccessor->count == vertexCount);
if (!convertDracoVertexAttributes(srcAccessor->component_type,
dracoAttr,
vertexCount,
&((uint8_t*) bufferView->data)[attributeOffset]))
{
TF_RUNTIME_ERROR("failed to decode Draco attribute");
return false;
}

srcAccessor->buffer_view = draco->buffer_view;
srcAccessor->offset = attributeOffset;

attributeOffset += vertexCount * srcAccessor->stride;
}
}

return true;
}
#endif

// Based on https://github.com/jkuhlmann/cgltf/pull/129
cgltf_result decompressMeshopt(cgltf_data* data)
{
Expand Down Expand Up @@ -241,6 +462,8 @@ namespace guc
}

bool meshoptCompressionRequired = false;
[[maybe_unused]]
bool dracoMeshCompressionRequired = false;

for (size_t i = 0; i < (*data)->extensions_required_count; i++)
{
Expand All @@ -252,6 +475,11 @@ namespace guc
meshoptCompressionRequired = true;
}

if (strcmp(ext, detail::GLTF_KHR_DRACO_MESH_COMPRESSION_EXTENSION_NAME) == 0)
{
dracoMeshCompressionRequired = true;
}

if (detail::extensionSupported(ext))
{
continue;
Expand All @@ -278,6 +506,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];
Expand Down

0 comments on commit 4ef66a3

Please sign in to comment.