From 3012446f02781e5208e55a8c997041b782955a74 Mon Sep 17 00:00:00 2001 From: Jim Eckerlein Date: Mon, 7 Dec 2020 15:23:55 +0100 Subject: glTF: update Draco library to new version To support decoding and enhanced encoding of Draco compressed glTF files. Differential Revision: https://developer.blender.org/D9642 --- extern/draco/src/common.cpp | 77 ++++++++++ extern/draco/src/common.h | 50 ++++++ extern/draco/src/decoder.cpp | 222 +++++++++++++++++++++++++++ extern/draco/src/decoder.h | 53 +++++++ extern/draco/src/draco-compressor.cpp | 277 ---------------------------------- extern/draco/src/draco-compressor.h | 173 --------------------- extern/draco/src/encoder.cpp | 247 ++++++++++++++++++++++++++++++ extern/draco/src/encoder.h | 51 +++++++ 8 files changed, 700 insertions(+), 450 deletions(-) create mode 100644 extern/draco/src/common.cpp create mode 100644 extern/draco/src/common.h create mode 100644 extern/draco/src/decoder.cpp create mode 100644 extern/draco/src/decoder.h delete mode 100644 extern/draco/src/draco-compressor.cpp delete mode 100644 extern/draco/src/draco-compressor.h create mode 100644 extern/draco/src/encoder.cpp create mode 100644 extern/draco/src/encoder.h (limited to 'extern/draco/src') diff --git a/extern/draco/src/common.cpp b/extern/draco/src/common.cpp new file mode 100644 index 00000000000..6f98d8db7ef --- /dev/null +++ b/extern/draco/src/common.cpp @@ -0,0 +1,77 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#include + +size_t getNumberOfComponents(char *dataType) +{ + if (!strcmp(dataType, "SCALAR")) + { + return 1; + } + if (!strcmp(dataType, "VEC2")) + { + return 2; + } + if (!strcmp(dataType, "VEC3")) + { + return 3; + } + if (!strcmp(dataType, "VEC4")) + { + return 4; + } + if (!strcmp(dataType, "MAT2")) + { + return 4; + } + if (!strcmp(dataType, "MAT3")) + { + return 9; + } + if (!strcmp(dataType, "MAT4")) + { + return 16; + } + + return 0; +} + +size_t getComponentByteLength(size_t componentType) +{ + switch (componentType) + { + case ComponentType::Byte: + case ComponentType::UnsignedByte: + return 1; + + case ComponentType::Short: + case ComponentType::UnsignedShort: + return 2; + + case ComponentType::UnsignedInt: + case ComponentType::Float: + return 4; + + default: + return 0; + } +} + +size_t getAttributeStride(size_t componentType, char *dataType) +{ + return getComponentByteLength(componentType) * getNumberOfComponents(dataType); +} diff --git a/extern/draco/src/common.h b/extern/draco/src/common.h new file mode 100644 index 00000000000..beaf7d91adb --- /dev/null +++ b/extern/draco/src/common.h @@ -0,0 +1,50 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Library for the Draco encoding/decoding feature inside the glTF-Blender-IO project. + * + * The python script within glTF-Blender-IO uses the CTypes library to open the DLL, + * load function pointers add pass the raw data to the encoder. + * + * @author Jim Eckerlein + * @date 2020-11-18 + */ + +#pragma once + +#include +#include + +#if defined(_MSC_VER) +#define API(returnType) extern "C" __declspec(dllexport) returnType __cdecl +#else +#define API(returnType) extern "C" returnType +#endif + +enum ComponentType: size_t +{ + Byte = 5120, + UnsignedByte = 5121, + Short = 5122, + UnsignedShort = 5123, + UnsignedInt = 5125, + Float = 5126, +}; + +size_t getNumberOfComponents(char *dataType); + +size_t getComponentByteLength(size_t componentType); + +size_t getAttributeStride(size_t componentType, char *dataType); diff --git a/extern/draco/src/decoder.cpp b/extern/draco/src/decoder.cpp new file mode 100644 index 00000000000..3f3e03bd9f2 --- /dev/null +++ b/extern/draco/src/decoder.cpp @@ -0,0 +1,222 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author Jim Eckerlein + * @date 2020-11-18 + */ + + +#include "decoder.h" + +#include +#include +#include + +#include "draco/mesh/mesh.h" +#include "draco/core/decoder_buffer.h" +#include "draco/compression/decode.h" + +#define LOG_PREFIX "DracoDecoder | " + +struct Decoder { + std::unique_ptr mesh; + std::vector indexBuffer; + std::map> buffers; + draco::DecoderBuffer decoderBuffer; + uint32_t vertexCount; + uint32_t indexCount; +}; + +Decoder *decoderCreate() +{ + return new Decoder; +} + +void decoderRelease(Decoder *decoder) +{ + delete decoder; +} + +bool decoderDecode(Decoder *decoder, void *data, size_t byteLength) +{ + draco::Decoder dracoDecoder; + draco::DecoderBuffer dracoDecoderBuffer; + dracoDecoderBuffer.Init(reinterpret_cast(data), byteLength); + + auto decoderStatus = dracoDecoder.DecodeMeshFromBuffer(&dracoDecoderBuffer); + if (!decoderStatus.ok()) + { + printf(LOG_PREFIX "Error during Draco decoding: %s\n", decoderStatus.status().error_msg()); + return false; + } + + decoder->mesh = std::move(decoderStatus).value(); + decoder->vertexCount = decoder->mesh->num_points(); + decoder->indexCount = decoder->mesh->num_faces() * 3; + + printf(LOG_PREFIX "Decoded %" PRIu32 " vertices, %" PRIu32 " indices\n", decoder->vertexCount, decoder->indexCount); + + return true; +} + +uint32_t decoderGetVertexCount(Decoder *decoder) +{ + return decoder->vertexCount; +} + +uint32_t decoderGetIndexCount(Decoder *decoder) +{ + return decoder->indexCount; +} + +bool decoderAttributeIsNormalized(Decoder *decoder, uint32_t id) +{ + const draco::PointAttribute* attribute = decoder->mesh->GetAttributeByUniqueId(id); + return attribute != nullptr && attribute->normalized(); +} + +bool decoderReadAttribute(Decoder *decoder, uint32_t id, size_t componentType, char *dataType) +{ + const draco::PointAttribute* attribute = decoder->mesh->GetAttributeByUniqueId(id); + + if (attribute == nullptr) + { + printf(LOG_PREFIX "Attribute with id=%" PRIu32 " does not exist in Draco data\n", id); + return false; + } + + size_t stride = getAttributeStride(componentType, dataType); + + std::vector decodedData; + decodedData.resize(stride * decoder->vertexCount); + + for (uint32_t i = 0; i < decoder->vertexCount; ++i) + { + auto index = attribute->mapped_index(draco::PointIndex(i)); + uint8_t *value = decodedData.data() + i * stride; + + bool converted = false; + + switch (componentType) + { + case ComponentType::Byte: + converted = attribute->ConvertValue(index, reinterpret_cast(value)); + break; + case ComponentType::UnsignedByte: + converted = attribute->ConvertValue(index, reinterpret_cast(value)); + break; + case ComponentType::Short: + converted = attribute->ConvertValue(index, reinterpret_cast(value)); + break; + case ComponentType::UnsignedShort: + converted = attribute->ConvertValue(index, reinterpret_cast(value)); + break; + case ComponentType::UnsignedInt: + converted = attribute->ConvertValue(index, reinterpret_cast(value)); + break; + case ComponentType::Float: + converted = attribute->ConvertValue(index, reinterpret_cast(value)); + break; + default: + break; + } + + if (!converted) + { + printf(LOG_PREFIX "Failed to convert Draco attribute type to glTF accessor type for attribute with id=%" PRIu32 "\n", id); + return false; + } + } + + decoder->buffers[id] = decodedData; + return true; +} + +size_t decoderGetAttributeByteLength(Decoder *decoder, size_t id) +{ + auto iter = decoder->buffers.find(id); + if (iter != decoder->buffers.end()) + { + return iter->second.size(); + } + else + { + return 0; + } +} + +void decoderCopyAttribute(Decoder *decoder, size_t id, void *output) +{ + auto iter = decoder->buffers.find(id); + if (iter != decoder->buffers.end()) + { + memcpy(output, iter->second.data(), iter->second.size()); + } +} + +template +void decodeIndices(Decoder *decoder) +{ + std::vector decodedIndices; + decodedIndices.resize(decoder->indexCount * sizeof(T)); + T *typedView = reinterpret_cast(decodedIndices.data()); + + for (uint32_t faceIndex = 0; faceIndex < decoder->mesh->num_faces(); ++faceIndex) + { + const draco::Mesh::Face &face = decoder->mesh->face(draco::FaceIndex(faceIndex)); + typedView[faceIndex * 3 + 0] = face[0].value(); + typedView[faceIndex * 3 + 1] = face[1].value(); + typedView[faceIndex * 3 + 2] = face[2].value(); + } + + decoder->indexBuffer = decodedIndices; +} + +bool decoderReadIndices(Decoder *decoder, size_t indexComponentType) +{ + switch (indexComponentType) + { + case ComponentType::Byte: + decodeIndices(decoder); + break; + case ComponentType::UnsignedByte: + decodeIndices(decoder); + break; + case ComponentType::Short: + decodeIndices(decoder); + break; + case ComponentType::UnsignedShort: + decodeIndices(decoder); + break; + case ComponentType::UnsignedInt: + decodeIndices(decoder); + break; + default: + printf(LOG_PREFIX "Index component type %zu not supported\n", indexComponentType); + return false; + } + + return true; +} + +size_t decoderGetIndicesByteLength(Decoder *decoder) +{ + return decoder->indexBuffer.size(); +} + +void decoderCopyIndices(Decoder *decoder, void *output) +{ + memcpy(output, decoder->indexBuffer.data(), decoder->indexBuffer.size()); +} diff --git a/extern/draco/src/decoder.h b/extern/draco/src/decoder.h new file mode 100644 index 00000000000..914eb776e8f --- /dev/null +++ b/extern/draco/src/decoder.h @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Library for the Draco encoding/decoding feature inside the glTF-Blender-IO project. + * + * The python script within glTF-Blender-IO uses the CTypes library to open the DLL, + * load function pointers add pass the raw data to the encoder. + * + * @author Jim Eckerlein + * @date 2020-11-18 + */ + +#pragma once + +#include "common.h" + +struct Decoder; + +API(Decoder *) decoderCreate(); + +API(void) decoderRelease(Decoder *decoder); + +API(bool) decoderDecode(Decoder *decoder, void *data, size_t byteLength); + +API(uint32_t) decoderGetVertexCount(Decoder *decoder); + +API(uint32_t) decoderGetIndexCount(Decoder *decoder); + +API(bool) decoderAttributeIsNormalized(Decoder *decoder, uint32_t id); + +API(bool) decoderReadAttribute(Decoder *decoder, uint32_t id, size_t componentType, char *dataType); + +API(size_t) decoderGetAttributeByteLength(Decoder *decoder, size_t id); + +API(void) decoderCopyAttribute(Decoder *decoder, size_t id, void *output); + +API(bool) decoderReadIndices(Decoder *decoder, size_t indexComponentType); + +API(size_t) decoderGetIndicesByteLength(Decoder *decoder); + +API(void) decoderCopyIndices(Decoder *decoder, void *output); diff --git a/extern/draco/src/draco-compressor.cpp b/extern/draco/src/draco-compressor.cpp deleted file mode 100644 index 4ae528888fe..00000000000 --- a/extern/draco/src/draco-compressor.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @author Jim Eckerlein - * @date 2019-11-29 - */ - -#include "draco-compressor.h" - -#include -#include - -#include "draco/mesh/mesh.h" -#include "draco/core/encoder_buffer.h" -#include "draco/compression/encode.h" - -/** - * Prefix used for logging messages. - */ -const char *logTag = "DRACO-COMPRESSOR"; - -struct DracoCompressor { - draco::Mesh mesh; - - // One data buffer per attribute. - std::vector> buffers; - - // The buffer the mesh is compressed into. - draco::EncoderBuffer encoderBuffer; - - // Level of compression [0-10]. - // Higher values mean slower encoding. - uint32_t compressionLevel = 7; - - struct { - uint32_t positions = 14; - uint32_t normals = 10; - uint32_t uvs = 12; - uint32_t generic = 12; - } quantization; -}; - -DracoCompressor *create_compressor() { - return new DracoCompressor; -} - -void set_compression_level( - DracoCompressor *const compressor, - uint32_t const compressionLevel -) { - compressor->compressionLevel = compressionLevel; -} - -void set_position_quantization( - DracoCompressor *const compressor, - uint32_t const quantizationBitsPosition -) { - compressor->quantization.positions = quantizationBitsPosition; -} - -void set_normal_quantization( - DracoCompressor *const compressor, - uint32_t const quantizationBitsNormal -) { - compressor->quantization.normals = quantizationBitsNormal; -} - -void set_uv_quantization( - DracoCompressor *const compressor, - uint32_t const quantizationBitsTexCoord -) { - compressor->quantization.uvs = quantizationBitsTexCoord; -} - -void set_generic_quantization( - DracoCompressor *const compressor, - uint32_t const bits -) { - compressor->quantization.generic = bits; -} - -bool compress( - DracoCompressor *const compressor -) { - draco::Encoder encoder; - - encoder.SetSpeedOptions(10 - (int)compressor->compressionLevel, 10 - (int)compressor->compressionLevel); - encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, compressor->quantization.positions); - encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, compressor->quantization.normals); - encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, compressor->quantization.uvs); - encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, compressor->quantization.generic); - - return encoder.EncodeMeshToBuffer(compressor->mesh, &compressor->encoderBuffer).ok(); -} - -bool compress_morphed( - DracoCompressor *const compressor -) { - draco::Encoder encoder; - - encoder.SetSpeedOptions(10 - (int)compressor->compressionLevel, 10 - (int)compressor->compressionLevel); - - // For some reason, `EncodeMeshToBuffer` crashes when not disabling prediction or when enabling quantization - // for attributes other position. - encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, compressor->quantization.positions); - 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); - - // Enforce triangle order preservation. - encoder.SetEncodingMethod(draco::MESH_SEQUENTIAL_ENCODING); - - return encoder.EncodeMeshToBuffer(compressor->mesh, &compressor->encoderBuffer).ok(); -} - -uint64_t get_compressed_size( - DracoCompressor const *const compressor -) { - return compressor->encoderBuffer.size(); -} - -void copy_to_bytes( - DracoCompressor const *const compressor, - uint8_t *const o_data -) { - memcpy(o_data, compressor->encoderBuffer.data(), compressor->encoderBuffer.size()); -} - -void destroy_compressor( - DracoCompressor *const compressor -) { - delete compressor; -} - -template -void set_faces_impl( - draco::Mesh &mesh, - int const index_count, - T const *const indices -) { - mesh.SetNumFaces((size_t) index_count / 3); - - for (int i = 0; i < index_count; i += 3) - { - const auto a = draco::PointIndex(indices[i]); - const auto b = draco::PointIndex(indices[i + 1]); - const auto c = draco::PointIndex(indices[i + 2]); - mesh.SetFace(draco::FaceIndex((uint32_t) i), {a, b, c}); - } -} - -void set_faces( - DracoCompressor *const compressor, - uint32_t const index_count, - uint32_t const index_byte_length, - uint8_t const *const indices -) { - switch (index_byte_length) - { - case 1: - { - set_faces_impl(compressor->mesh, index_count, (uint8_t *) indices); - break; - } - case 2: - { - set_faces_impl(compressor->mesh, index_count, (uint16_t *) indices); - break; - } - case 4: - { - set_faces_impl(compressor->mesh, index_count, (uint32_t *) indices); - break; - } - default: - { - printf("%s: Unsupported index size %d\n", logTag, index_byte_length); - break; - } - } -} - -uint32_t add_attribute_to_mesh( - DracoCompressor *const compressor, - draco::GeometryAttribute::Type const semantics, - draco::DataType const data_type, - uint32_t const count, - uint8_t const component_count, - uint8_t const component_size, - uint8_t const *const data -) { - auto buffer = std::make_unique(); - - draco::GeometryAttribute attribute; - - attribute.Init( - semantics, - &*buffer, - component_count, - data_type, - false, - component_size * component_count, - 0 - ); - - auto const id = (uint32_t)compressor->mesh.AddAttribute(attribute, true, count); - - for (uint32_t i = 0; i < count; i++) - { - compressor->mesh.attribute(id)->SetAttributeValue( - draco::AttributeValueIndex(i), - data + i * component_count * component_size - ); - } - - 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); -} - -uint32_t add_uvs_f32( - DracoCompressor *const compressor, - uint32_t const count, - uint8_t const *const data -) { - return add_attribute_to_mesh(compressor, draco::GeometryAttribute::TEX_COORD, - draco::DT_FLOAT32, count, 2, sizeof(float), data); -} - -uint32_t add_joints_u16( - DracoCompressor *compressor, - uint32_t const count, - uint8_t const *const data -) { - return add_attribute_to_mesh(compressor, draco::GeometryAttribute::GENERIC, - draco::DT_UINT16, count, 4, sizeof(uint16_t), data); -} - -uint32_t add_weights_f32( - DracoCompressor *compressor, - uint32_t const count, - uint8_t const *const data -) { - return add_attribute_to_mesh(compressor, draco::GeometryAttribute::GENERIC, - draco::DT_FLOAT32, count, 4, sizeof(float), data); -} diff --git a/extern/draco/src/draco-compressor.h b/extern/draco/src/draco-compressor.h deleted file mode 100644 index fb6168a61af..00000000000 --- a/extern/draco/src/draco-compressor.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * C++ library for the Draco compression feature inside the glTF-Blender-IO project. - * - * The python side uses the CTypes library 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 - * @date 2019-11-29 - */ - -#include - -#if defined(_MSC_VER) -#define DLL_EXPORT(retType) extern "C" __declspec(dllexport) retType __cdecl -#else -#define DLL_EXPORT(retType) extern "C" retType -#endif - -/** - * This tuple is opaquely exposed to Python through a pointer. - * It encapsulates the complete current compressor state. - * - * A single instance is only intended to compress a single primitive. - */ -struct DracoCompressor; - -DLL_EXPORT(DracoCompressor *) -create_compressor (); - -DLL_EXPORT(void) -set_compression_level ( - DracoCompressor *compressor, - uint32_t compressionLevel -); - -DLL_EXPORT(void) -set_position_quantization ( - DracoCompressor *compressor, - uint32_t quantizationBitsPosition -); - -DLL_EXPORT(void) -set_normal_quantization ( - DracoCompressor *compressor, - uint32_t quantizationBitsNormal -); - -DLL_EXPORT(void) -set_uv_quantization ( - DracoCompressor *compressor, - uint32_t quantizationBitsTexCoord -); - -DLL_EXPORT(void) -set_generic_quantization ( - DracoCompressor *compressor, - uint32_t bits -); - -/// Compresses a mesh. -/// Use `compress_morphed` when compressing primitives which have morph targets. -DLL_EXPORT(bool) -compress ( - DracoCompressor *compressor -); - -/// Compresses the mesh. -/// Use this instead of `compress`, because this procedure takes into account that mesh triangles must not be reordered. -DLL_EXPORT(bool) -compress_morphed ( - DracoCompressor *compressor -); - -/** - * Returns the size of the compressed data in bytes. - */ -DLL_EXPORT(uint64_t) -get_compressed_size ( - DracoCompressor const *compressor -); - -/** - * Copies the compressed mesh into the given byte buffer. - * - * @param[o_data] A Python `bytes` object. - */ -DLL_EXPORT(void) -copy_to_bytes ( - DracoCompressor const *compressor, - uint8_t *o_data -); - -/** - * Releases all memory allocated by the compressor. - */ -DLL_EXPORT(void) -destroy_compressor ( - DracoCompressor *compressor -); - -DLL_EXPORT(void) -set_faces ( - DracoCompressor *compressor, - uint32_t index_count, - uint32_t index_byte_length, - uint8_t const *indices -); - -/// Adds a `float` position attribute to the current mesh. -/// Returns the id Draco has assigned to this attribute. -DLL_EXPORT(uint32_t) -add_positions_f32 ( - DracoCompressor *compressor, - uint32_t count, - uint8_t const *data -); - -/// Adds a `float` normal attribute to the current mesh. -/// Returns the id Draco has assigned to this attribute. -DLL_EXPORT(uint32_t) -add_normals_f32 ( - DracoCompressor *compressor, - uint32_t count, - uint8_t const *data -); - -/// Adds a `float` texture coordinate attribute to the current mesh. -/// Returns the id Draco has assigned to this attribute. -DLL_EXPORT(uint32_t) -add_uvs_f32 ( - DracoCompressor *compressor, - uint32_t count, - uint8_t const *data -); - -/// Adds a `unsigned short` joint attribute to the current mesh. -/// Returns the id Draco has assigned to this attribute. -DLL_EXPORT(uint32_t) -add_joints_u16 ( - DracoCompressor *compressor, - uint32_t count, - uint8_t const *data -); - -/// Adds a `float` weight attribute to the current mesh. -/// Returns the id Draco has assigned to this attribute. -DLL_EXPORT(uint32_t) -add_weights_f32 ( - DracoCompressor *compressor, - uint32_t count, - uint8_t const *data -); diff --git a/extern/draco/src/encoder.cpp b/extern/draco/src/encoder.cpp new file mode 100644 index 00000000000..ff7570ecfcd --- /dev/null +++ b/extern/draco/src/encoder.cpp @@ -0,0 +1,247 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author Jim Eckerlein + * @date 2019-11-18 + */ + +#include "encoder.h" + +#include +#include + +#include "draco/mesh/mesh.h" +#include "draco/core/encoder_buffer.h" +#include "draco/compression/encode.h" + +#define LOG_PREFIX "DracoEncoder | " + +struct Encoder +{ + draco::Mesh mesh; + uint32_t encodedVertices; + uint32_t encodedIndices; + std::vector> buffers; + draco::EncoderBuffer encoderBuffer; + uint32_t compressionLevel = 7; + size_t rawSize = 0; + struct + { + uint32_t position = 14; + uint32_t normal = 10; + uint32_t uv = 12; + uint32_t color = 10; + uint32_t generic = 12; + } quantization; +}; + +Encoder *encoderCreate(uint32_t vertexCount) +{ + Encoder *encoder = new Encoder; + encoder->mesh.set_num_points(vertexCount); + return encoder; +} + +void encoderRelease(Encoder *encoder) +{ + delete encoder; +} + +void encoderSetCompressionLevel(Encoder *encoder, uint32_t compressionLevel) { + encoder->compressionLevel = compressionLevel; +} + +void encoderSetQuantizationBits(Encoder *encoder, uint32_t position, uint32_t normal, uint32_t uv, uint32_t color, uint32_t generic) +{ + encoder->quantization.position = position; + encoder->quantization.normal = normal; + encoder->quantization.uv = uv; + encoder->quantization.color = color; + encoder->quantization.generic = generic; +} + +bool encoderEncode(Encoder *encoder, uint8_t preserveTriangleOrder) +{ + printf(LOG_PREFIX "Preserve triangle order: %s\n", preserveTriangleOrder ? "yes" : "no"); + + draco::Encoder dracoEncoder; + + int speed = 10 - static_cast(encoder->compressionLevel); + dracoEncoder.SetSpeedOptions(speed, speed); + + dracoEncoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, encoder->quantization.position); + dracoEncoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, encoder->quantization.normal); + dracoEncoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, encoder->quantization.uv); + dracoEncoder.SetAttributeQuantization(draco::GeometryAttribute::COLOR, encoder->quantization.color); + dracoEncoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, encoder->quantization.generic); + dracoEncoder.SetTrackEncodedProperties(true); + + if (preserveTriangleOrder) + { + dracoEncoder.SetEncodingMethod(draco::MESH_SEQUENTIAL_ENCODING); + } + + auto encoderStatus = dracoEncoder.EncodeMeshToBuffer(encoder->mesh, &encoder->encoderBuffer); + if (encoderStatus.ok()) + { + encoder->encodedVertices = static_cast(dracoEncoder.num_encoded_points()); + encoder->encodedIndices = static_cast(dracoEncoder.num_encoded_faces() * 3); + size_t encodedSize = encoder->encoderBuffer.size(); + float compressionRatio = static_cast(encoder->rawSize) / static_cast(encodedSize); + printf(LOG_PREFIX "Encoded %" PRIu32 " vertices, %" PRIu32 " indices, raw size: %zu, encoded size: %zu, compression ratio: %.2f\n", encoder->encodedVertices, encoder->encodedIndices, encoder->rawSize, encodedSize, compressionRatio); + return true; + } + else + { + printf(LOG_PREFIX "Error during Draco encoding: %s\n", encoderStatus.error_msg()); + return false; + } +} + +uint32_t encoderGetEncodedVertexCount(Encoder *encoder) +{ + return encoder->encodedVertices; +} + +uint32_t encoderGetEncodedIndexCount(Encoder *encoder) +{ + return encoder->encodedIndices; +} + +uint64_t encoderGetByteLength(Encoder *encoder) +{ + return encoder->encoderBuffer.size(); +} + +void encoderCopy(Encoder *encoder, uint8_t *data) +{ + memcpy(data, encoder->encoderBuffer.data(), encoder->encoderBuffer.size()); +} + +template +void encodeIndices(Encoder *encoder, uint32_t indexCount, T *indices) +{ + int face_count = indexCount / 3; + encoder->mesh.SetNumFaces(static_cast(face_count)); + encoder->rawSize += indexCount * sizeof(T); + + for (int i = 0; i < face_count; ++i) + { + draco::Mesh::Face face = { + draco::PointIndex(indices[3 * i + 0]), + draco::PointIndex(indices[3 * i + 1]), + draco::PointIndex(indices[3 * i + 2]) + }; + encoder->mesh.SetFace(draco::FaceIndex(static_cast(i)), face); + } +} + +void encoderSetIndices(Encoder *encoder, size_t indexComponentType, uint32_t indexCount, void *indices) +{ + switch (indexComponentType) + { + case ComponentType::Byte: + encodeIndices(encoder, indexCount, reinterpret_cast(indices)); + break; + case ComponentType::UnsignedByte: + encodeIndices(encoder, indexCount, reinterpret_cast(indices)); + break; + case ComponentType::Short: + encodeIndices(encoder, indexCount, reinterpret_cast(indices)); + break; + case ComponentType::UnsignedShort: + encodeIndices(encoder, indexCount, reinterpret_cast(indices)); + break; + case ComponentType::UnsignedInt: + encodeIndices(encoder, indexCount, reinterpret_cast(indices)); + break; + default: + printf(LOG_PREFIX "Index component type %zu not supported\n", indexComponentType); + } +} + +draco::GeometryAttribute::Type getAttributeSemantics(char *attribute) +{ + if (!strcmp(attribute, "POSITION")) + { + return draco::GeometryAttribute::POSITION; + } + if (!strcmp(attribute, "NORMAL")) + { + return draco::GeometryAttribute::NORMAL; + } + if (!strncmp(attribute, "TEXCOORD", strlen("TEXCOORD"))) + { + return draco::GeometryAttribute::TEX_COORD; + } + if (!strncmp(attribute, "COLOR", strlen("COLOR"))) + { + return draco::GeometryAttribute::COLOR; + } + + return draco::GeometryAttribute::GENERIC; +} + +draco::DataType getDataType(size_t componentType) +{ + switch (componentType) + { + case ComponentType::Byte: + return draco::DataType::DT_INT8; + + case ComponentType::UnsignedByte: + return draco::DataType::DT_UINT8; + + case ComponentType::Short: + return draco::DataType::DT_INT16; + + case ComponentType::UnsignedShort: + return draco::DataType::DT_UINT16; + + case ComponentType::UnsignedInt: + return draco::DataType::DT_UINT32; + + case ComponentType::Float: + return draco::DataType::DT_FLOAT32; + + default: + return draco::DataType::DT_INVALID; + } +} + +API(uint32_t) encoderSetAttribute(Encoder *encoder, char *attributeName, size_t componentType, char *dataType, void *data) +{ + auto buffer = std::make_unique(); + uint32_t count = encoder->mesh.num_points(); + size_t componentCount = getNumberOfComponents(dataType); + size_t stride = getAttributeStride(componentType, dataType); + draco::DataType dracoDataType = getDataType(componentType); + + draco::GeometryAttribute::Type semantics = getAttributeSemantics(attributeName); + draco::GeometryAttribute attribute; + attribute.Init(semantics, &*buffer, componentCount, getDataType(componentType), false, stride, 0); + + auto id = static_cast(encoder->mesh.AddAttribute(attribute, true, count)); + auto dataBytes = reinterpret_cast(data); + + for (uint32_t i = 0; i < count; i++) + { + encoder->mesh.attribute(id)->SetAttributeValue(draco::AttributeValueIndex(i), dataBytes + i * stride); + } + + encoder->buffers.emplace_back(std::move(buffer)); + encoder->rawSize += count * stride; + return id; +} diff --git a/extern/draco/src/encoder.h b/extern/draco/src/encoder.h new file mode 100644 index 00000000000..2f7f21a469b --- /dev/null +++ b/extern/draco/src/encoder.h @@ -0,0 +1,51 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Library for the Draco encoding/decoding feature inside the glTF-Blender-IO project. + * + * The python script within glTF-Blender-IO uses the CTypes library to open the DLL, + * load function pointers add pass the raw data to the encoder. + * + * @author Jim Eckerlein + * @date 2020-11-18 + */ + +#pragma once + +#include "common.h" + +struct Encoder; + +API(Encoder *) encoderCreate(uint32_t vertexCount); + +API(void) encoderRelease(Encoder *encoder); + +API(void) encoderSetCompressionLevel(Encoder *encoder, uint32_t compressionLevel); + +API(void) encoderSetQuantizationBits(Encoder *encoder, uint32_t position, uint32_t normal, uint32_t uv, uint32_t color, uint32_t generic); + +API(bool) encoderEncode(Encoder *encoder, uint8_t preserveTriangleOrder); + +API(uint64_t) encoderGetByteLength(Encoder *encoder); + +API(void) encoderCopy(Encoder *encoder, uint8_t *data); + +API(void) encoderSetIndices(Encoder *encoder, size_t indexComponentType, uint32_t indexCount, void *indices); + +API(uint32_t) encoderSetAttribute(Encoder *encoder, char *attributeName, size_t componentType, char *dataType, void *data); + +API(uint32_t) encoderGetEncodedVertexCount(Encoder *encoder); + +API(uint32_t) encoderGetEncodedIndexCount(Encoder *encoder); -- cgit v1.2.3