Changeset View
Changeset View
Standalone View
Standalone View
extern/draco/src/draco-compressor.cpp
| Context not available. | |||||
| */ | */ | ||||
| /** | /** | ||||
| * Implemententation for the Draco exporter from the C++ side. | |||||
| * | |||||
| * The python side uses the CTypes libary to open the DLL, load function | |||||
| * pointers add pass the data to the compressor as raw bytes. | |||||
| * | |||||
| * The compressor intercepts the regular GLTF exporter after data has been | |||||
| * gathered and right before the data is converted to a JSON representation, | |||||
| * which is going to be written out. | |||||
| * | |||||
| * The original uncompressed data is removed and replaces an extension, | |||||
| * pointing to the newly created buffer containing the compressed data. | |||||
| * | |||||
| * @author Jim Eckerlein <eckerlein@ux3d.io> | * @author Jim Eckerlein <eckerlein@ux3d.io> | ||||
| * @date 2019-01-15 | * @date 2019-11-29 | ||||
| */ | */ | ||||
| #include <iostream> | #include "draco-compressor.h" | ||||
| #include <fstream> | |||||
| #include <memory> | #include <memory> | ||||
| #include <sstream> | #include <vector> | ||||
| #include "draco/mesh/mesh.h" | #include "draco/mesh/mesh.h" | ||||
| #include "draco/point_cloud/point_cloud.h" | #include "draco/core/encoder_buffer.h" | ||||
| #include "draco/core/vector_d.h" | |||||
| #include "draco/io/mesh_io.h" | |||||
| #include "draco/compression/encode.h" | #include "draco/compression/encode.h" | ||||
| #if defined(_MSC_VER) | |||||
| #define DLL_EXPORT(retType) extern "C" __declspec(dllexport) retType __cdecl | |||||
| #else | |||||
| #define DLL_EXPORT(retType) extern "C" retType | |||||
| #endif | |||||
| const char *logTag = "DRACO-COMPRESSOR"; | |||||
| /** | /** | ||||
| * This tuple is opaquely exposed to Python through a pointer. | * Prefix used for logging messages. | ||||
| * It encapsulates the complete current compressor state. | |||||
| * | |||||
| * A single instance is only intended to compress a single primitive. | |||||
| */ | */ | ||||
| struct DracoCompressor { | const char *logTag = "DRACO-COMPRESSOR"; | ||||
| /** | struct DracoCompressor { | ||||
| * All positions, normals and texture coordinates are appended to this mesh. | |||||
| */ | |||||
| draco::Mesh mesh; | draco::Mesh mesh; | ||||
| /** | // One data buffer per attribute. | ||||
| * One data buffer per attribute. | |||||
| */ | |||||
| std::vector<std::unique_ptr<draco::DataBuffer>> buffers; | std::vector<std::unique_ptr<draco::DataBuffer>> buffers; | ||||
| /** | // The buffer the mesh is compressed into. | ||||
| * The buffer the mesh is compressed into. | |||||
| */ | |||||
| draco::EncoderBuffer encoderBuffer; | draco::EncoderBuffer encoderBuffer; | ||||
| /** | // Level of compression [0-10]. | ||||
| * The id Draco assigns to the position attribute. | // Higher values mean slower encoding. | ||||
| * Required to be reported in the GLTF file. | |||||
| */ | |||||
| uint32_t positionAttributeId = (uint32_t) -1; | |||||
| /** | |||||
| * The id Draco assigns to the normal attribute. | |||||
| * Required to be reported in the GLTF file. | |||||
| */ | |||||
| uint32_t normalAttributeId = (uint32_t) -1; | |||||
| /** | |||||
| * The ids Draco assigns to the texture coordinate attributes. | |||||
| * Required to be reported in the GLTF file. | |||||
| */ | |||||
| std::vector<uint32_t> texCoordAttributeIds; | |||||
| /** | |||||
| * Level of compression [0-10]. | |||||
| * Higher values mean slower encoding. | |||||
| */ | |||||
| uint32_t compressionLevel = 7; | uint32_t compressionLevel = 7; | ||||
| uint32_t quantizationBitsPosition = 14; | struct { | ||||
| uint32_t quantizationBitsNormal = 10; | uint32_t positions = 14; | ||||
| uint32_t quantizationBitsTexCoord = 12; | uint32_t normals = 10; | ||||
| uint32_t uvs = 12; | |||||
| uint32_t generic = 12; | |||||
| } quantization; | |||||
| }; | }; | ||||
| draco::GeometryAttribute createAttribute( | DracoCompressor *create_compressor() { | ||||
| draco::GeometryAttribute::Type type, | |||||
| draco::DataBuffer &buffer, | |||||
| uint8_t components | |||||
| ) { | |||||
| draco::GeometryAttribute attribute; | |||||
| attribute.Init( | |||||
| type, | |||||
| &buffer, | |||||
| components, | |||||
| draco::DataType::DT_FLOAT32, | |||||
| false, | |||||
| sizeof(float) * components, | |||||
| 0 | |||||
| ); | |||||
| return attribute; | |||||
| } | |||||
| DLL_EXPORT(DracoCompressor *) createCompressor() { | |||||
| return new DracoCompressor; | return new DracoCompressor; | ||||
| } | } | ||||
| DLL_EXPORT(void) setCompressionLevel( | void set_compression_level( | ||||
| DracoCompressor *compressor, | DracoCompressor *const compressor, | ||||
| uint32_t compressionLevel | uint32_t const compressionLevel | ||||
| ) { | ) { | ||||
| compressor->compressionLevel = compressionLevel; | compressor->compressionLevel = compressionLevel; | ||||
| } | } | ||||
| DLL_EXPORT(void) setPositionQuantizationBits( | void set_position_quantization( | ||||
| DracoCompressor *compressor, | DracoCompressor *const compressor, | ||||
| uint32_t quantizationBitsPosition | uint32_t const quantizationBitsPosition | ||||
| ) { | ) { | ||||
| compressor->quantizationBitsPosition = quantizationBitsPosition; | compressor->quantization.positions = quantizationBitsPosition; | ||||
| } | } | ||||
| DLL_EXPORT(void) setNormalQuantizationBits( | void set_normal_quantization( | ||||
| DracoCompressor *compressor, | DracoCompressor *const compressor, | ||||
| uint32_t quantizationBitsNormal | uint32_t const quantizationBitsNormal | ||||
| ) { | ) { | ||||
| compressor->quantizationBitsNormal = quantizationBitsNormal; | compressor->quantization.normals = quantizationBitsNormal; | ||||
| } | } | ||||
| DLL_EXPORT(void) setTexCoordQuantizationBits( | void set_uv_quantization( | ||||
| DracoCompressor *compressor, | DracoCompressor *const compressor, | ||||
| uint32_t quantizationBitsTexCoord | uint32_t const quantizationBitsTexCoord | ||||
| ) { | ) { | ||||
| compressor->quantizationBitsTexCoord = quantizationBitsTexCoord; | compressor->quantization.uvs = quantizationBitsTexCoord; | ||||
| } | } | ||||
| DLL_EXPORT(bool) compress( | void set_generic_quantization( | ||||
| DracoCompressor *compressor | DracoCompressor *const compressor, | ||||
| uint32_t const bits | |||||
| ) { | ) { | ||||
| printf("%s: Compressing primitive:\n", logTag); | compressor->quantization.generic = bits; | ||||
| printf("%s: Compression level [0-10]: %d\n", logTag, compressor->compressionLevel); | } | ||||
| printf("%s: Position quantization bits: %d\n", logTag, compressor->quantizationBitsPosition); | |||||
| printf("%s: Normal quantization bits: %d\n", logTag, compressor->quantizationBitsNormal); | |||||
| printf("%s: Position quantization bits: %d\n", logTag, compressor->quantizationBitsTexCoord); | |||||
| bool compress( | |||||
| DracoCompressor *const compressor | |||||
| ) { | |||||
| draco::Encoder encoder; | draco::Encoder encoder; | ||||
| encoder.SetSpeedOptions(10 - compressor->compressionLevel, 10 - compressor->compressionLevel); | encoder.SetSpeedOptions(10 - (int)compressor->compressionLevel, 10 - (int)compressor->compressionLevel); | ||||
| encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, compressor->quantizationBitsPosition); | encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, compressor->quantization.positions); | ||||
| encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, compressor->quantizationBitsNormal); | encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, compressor->quantization.normals); | ||||
| encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, compressor->quantizationBitsTexCoord); | encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, compressor->quantization.uvs); | ||||
| encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, compressor->quantization.generic); | |||||
| draco::Status result = encoder.EncodeMeshToBuffer(compressor->mesh, &compressor->encoderBuffer); | |||||
| if(!result.ok()) { | return encoder.EncodeMeshToBuffer(compressor->mesh, &compressor->encoderBuffer).ok(); | ||||
| printf("%s: Could not compress mesh: %s\n", logTag, result.error_msg()); | |||||
| return false; | |||||
| } | |||||
| else { | |||||
| return true; | |||||
| } | |||||
| } | } | ||||
| /** | bool compress_morphed( | ||||
| * Returns the size of the compressed data in bytes. | DracoCompressor *const compressor | ||||
| */ | |||||
| DLL_EXPORT(uint64_t) compressedSize( | |||||
| DracoCompressor *compressor | |||||
| ) { | ) { | ||||
| return compressor->encoderBuffer.size(); | draco::Encoder encoder; | ||||
| } | |||||
| /** | encoder.SetSpeedOptions(10 - (int)compressor->compressionLevel, 10 - (int)compressor->compressionLevel); | ||||
| * Copies the compressed mesh into the given byte buffer. | |||||
| * @param[o_data] A Python `bytes` object. | |||||
| * | |||||
| */ | |||||
| DLL_EXPORT(void) copyToBytes( | |||||
| DracoCompressor *compressor, | |||||
| uint8_t *o_data | |||||
| ) { | |||||
| memcpy(o_data, compressor->encoderBuffer.data(), compressedSize(compressor)); | |||||
| } | |||||
| DLL_EXPORT(uint32_t) getPositionAttributeId( | // For some reason, `EncodeMeshToBuffer` crashes when not disabling prediction or when enabling quantization | ||||
| DracoCompressor *compressor | // for attributes other position. | ||||
| ) { | encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, compressor->quantization.positions); | ||||
| return compressor->positionAttributeId; | encoder.SetAttributePredictionScheme(draco::GeometryAttribute::POSITION, draco::PREDICTION_NONE); | ||||
| } | encoder.SetAttributePredictionScheme(draco::GeometryAttribute::NORMAL, draco::PREDICTION_NONE); | ||||
| encoder.SetAttributePredictionScheme(draco::GeometryAttribute::TEX_COORD, draco::PREDICTION_NONE); | |||||
| encoder.SetAttributePredictionScheme(draco::GeometryAttribute::GENERIC, draco::PREDICTION_NONE); | |||||
| DLL_EXPORT(uint32_t) getNormalAttributeId( | // Enforce triangle order preservation. | ||||
| DracoCompressor *compressor | encoder.SetEncodingMethod(draco::MESH_SEQUENTIAL_ENCODING); | ||||
| ) { | |||||
| return compressor->normalAttributeId; | return encoder.EncodeMeshToBuffer(compressor->mesh, &compressor->encoderBuffer).ok(); | ||||
| } | } | ||||
| DLL_EXPORT(uint32_t) getTexCoordAttributeIdCount( | uint64_t get_compressed_size( | ||||
| DracoCompressor *compressor | DracoCompressor const *const compressor | ||||
| ) { | ) { | ||||
| return (uint32_t) compressor->texCoordAttributeIds.size(); | return compressor->encoderBuffer.size(); | ||||
| } | } | ||||
| DLL_EXPORT(uint32_t) getTexCoordAttributeId( | void copy_to_bytes( | ||||
| DracoCompressor *compressor, | DracoCompressor const *const compressor, | ||||
| uint32_t index | uint8_t *const o_data | ||||
| ) { | ) { | ||||
| return compressor->texCoordAttributeIds[index]; | memcpy(o_data, compressor->encoderBuffer.data(), compressor->encoderBuffer.size()); | ||||
| } | } | ||||
| /** | void destroy_compressor( | ||||
| * Releases all memory allocated by the compressor. | DracoCompressor *const compressor | ||||
| */ | |||||
| DLL_EXPORT(void) disposeCompressor( | |||||
| DracoCompressor *compressor | |||||
| ) { | ) { | ||||
| delete compressor; | delete compressor; | ||||
| } | } | ||||
| template<class T> | template<class T> | ||||
| void setFaces( | void set_faces_impl( | ||||
| draco::Mesh &mesh, | draco::Mesh &mesh, | ||||
| int numIndices, | int const index_count, | ||||
| T *indices | T const *const indices | ||||
| ) { | ) { | ||||
| mesh.SetNumFaces((size_t) numIndices / 3); | mesh.SetNumFaces((size_t) index_count / 3); | ||||
| for (int i = 0; i < numIndices; i += 3) | for (int i = 0; i < index_count; i += 3) | ||||
| { | { | ||||
| const auto a = draco::PointIndex(indices[i]); | const auto a = draco::PointIndex(indices[i]); | ||||
| const auto b = draco::PointIndex(indices[i + 1]); | const auto b = draco::PointIndex(indices[i + 1]); | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| DLL_EXPORT(void) setFaces( | void set_faces( | ||||
| DracoCompressor *compressor, | DracoCompressor *const compressor, | ||||
| uint32_t numIndices, | uint32_t const index_count, | ||||
| uint32_t bytesPerIndex, | uint32_t const index_byte_length, | ||||
| void *indices | uint8_t const *const indices | ||||
| ) { | ) { | ||||
| switch (bytesPerIndex) | switch (index_byte_length) | ||||
| { | { | ||||
| case 1: | case 1: | ||||
| { | { | ||||
| setFaces(compressor->mesh, numIndices, (uint8_t *) indices); | set_faces_impl(compressor->mesh, index_count, (uint8_t *) indices); | ||||
| break; | break; | ||||
| } | } | ||||
| case 2: | case 2: | ||||
| { | { | ||||
| setFaces(compressor->mesh, numIndices, (uint16_t *) indices); | set_faces_impl(compressor->mesh, index_count, (uint16_t *) indices); | ||||
| break; | break; | ||||
| } | } | ||||
| case 4: | case 4: | ||||
| { | { | ||||
| setFaces(compressor->mesh, numIndices, (uint32_t *) indices); | set_faces_impl(compressor->mesh, index_count, (uint32_t *) indices); | ||||
| break; | break; | ||||
| } | } | ||||
| default: | default: | ||||
| { | { | ||||
| printf("%s: Unsupported index size %d\n", logTag, bytesPerIndex); | printf("%s: Unsupported index size %d\n", logTag, index_byte_length); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void addFloatAttribute( | uint32_t add_attribute_to_mesh( | ||||
| DracoCompressor *compressor, | DracoCompressor *const compressor, | ||||
| draco::GeometryAttribute::Type type, | draco::GeometryAttribute::Type const semantics, | ||||
| uint32_t count, | draco::DataType const data_type, | ||||
| uint8_t componentCount, | uint32_t const count, | ||||
| float *source | uint8_t const component_count, | ||||
| uint8_t const component_size, | |||||
| uint8_t const *const data | |||||
| ) { | ) { | ||||
| auto buffer = std::make_unique<draco::DataBuffer>(); | auto buffer = std::make_unique<draco::DataBuffer>(); | ||||
| const auto attribute = createAttribute(type, *buffer, componentCount); | draco::GeometryAttribute attribute; | ||||
| const auto id = (const uint32_t) compressor->mesh.AddAttribute(attribute, false, count); | attribute.Init( | ||||
| compressor->mesh.attribute(id)->SetIdentityMapping(); | semantics, | ||||
| &*buffer, | |||||
| component_count, | |||||
| data_type, | |||||
| false, | |||||
| component_size * component_count, | |||||
| 0 | |||||
| ); | |||||
| switch (type) | auto const id = (uint32_t)compressor->mesh.AddAttribute(attribute, true, count); | ||||
| { | |||||
| case draco::GeometryAttribute::POSITION: | |||||
| compressor->positionAttributeId = id; | |||||
| break; | |||||
| case draco::GeometryAttribute::NORMAL: | |||||
| compressor->normalAttributeId = id; | |||||
| break; | |||||
| case draco::GeometryAttribute::TEX_COORD: | |||||
| compressor->texCoordAttributeIds.push_back(id); | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| for (uint32_t i = 0; i < count; i++) | for (uint32_t i = 0; i < count; i++) | ||||
| { | { | ||||
| compressor->mesh.attribute(id)->SetAttributeValue( | compressor->mesh.attribute(id)->SetAttributeValue( | ||||
| draco::AttributeValueIndex(i), | draco::AttributeValueIndex(i), | ||||
| source + i * componentCount | data + i * component_count * component_size | ||||
| ); | ); | ||||
| } | } | ||||
| compressor->buffers.emplace_back(std::move(buffer)); | compressor->buffers.emplace_back(std::move(buffer)); | ||||
| return id; | |||||
| } | |||||
| uint32_t add_positions_f32( | |||||
| DracoCompressor *const compressor, | |||||
| uint32_t const count, | |||||
| uint8_t const *const data | |||||
| ) { | |||||
| return add_attribute_to_mesh(compressor, draco::GeometryAttribute::POSITION, | |||||
| draco::DT_FLOAT32, count, 3, sizeof(float), data); | |||||
| } | |||||
| uint32_t add_normals_f32( | |||||
| DracoCompressor *const compressor, | |||||
| uint32_t const count, | |||||
| uint8_t const *const data | |||||
| ) { | |||||
| return add_attribute_to_mesh(compressor, draco::GeometryAttribute::NORMAL, | |||||
| draco::DT_FLOAT32, count, 3, sizeof(float), data); | |||||
| } | } | ||||
| DLL_EXPORT(void) addPositionAttribute( | uint32_t add_uvs_f32( | ||||
| DracoCompressor *compressor, | DracoCompressor *const compressor, | ||||
| uint32_t count, | uint32_t const count, | ||||
| float *source | uint8_t const *const data | ||||
| ) { | ) { | ||||
| addFloatAttribute(compressor, draco::GeometryAttribute::POSITION, count, 3, source); | return add_attribute_to_mesh(compressor, draco::GeometryAttribute::TEX_COORD, | ||||
| draco::DT_FLOAT32, count, 2, sizeof(float), data); | |||||
| } | } | ||||
| DLL_EXPORT(void) addNormalAttribute( | uint32_t add_joints_u16( | ||||
| DracoCompressor *compressor, | DracoCompressor *compressor, | ||||
| uint32_t count, | uint32_t const count, | ||||
| float *source | uint8_t const *const data | ||||
| ) { | ) { | ||||
| addFloatAttribute(compressor, draco::GeometryAttribute::NORMAL, count, 3, source); | return add_attribute_to_mesh(compressor, draco::GeometryAttribute::GENERIC, | ||||
| draco::DT_UINT16, count, 4, sizeof(uint16_t), data); | |||||
| } | } | ||||
| DLL_EXPORT(void) addTexCoordAttribute( | uint32_t add_weights_f32( | ||||
| DracoCompressor *compressor, | DracoCompressor *compressor, | ||||
| uint32_t count, | uint32_t const count, | ||||
| float *source | uint8_t const *const data | ||||
| ) { | ) { | ||||
| addFloatAttribute(compressor, draco::GeometryAttribute::TEX_COORD, count, 2, source); | return add_attribute_to_mesh(compressor, draco::GeometryAttribute::GENERIC, | ||||
| draco::DT_FLOAT32, count, 4, sizeof(float), data); | |||||
| } | } | ||||
| Context not available. | |||||