glTF: upgrade Draco to version 1.3.5 and add mesh skinning support

This will fix exporting meshes with armatures using Draco compression, like:
https://github.com/KhronosGroup/glTF-Blender-IO/issues/617

Differential Revision: https://developer.blender.org/D6342
This commit is contained in:
Jim Eckerlein 2019-12-05 19:11:53 +01:00 committed by Brecht Van Lommel
parent 05ef758f46
commit 9febff7e14
81 changed files with 1215 additions and 666 deletions

View File

@ -24,6 +24,6 @@ set(CMAKE_CXX_STANDARD 14)
add_subdirectory(dracoenc)
# Build blender-draco-exporter module.
add_library(extern_draco SHARED src/draco-compressor.cpp)
add_library(extern_draco SHARED src/draco-compressor.cpp src/draco-compressor.h)
target_include_directories(extern_draco PUBLIC dracoenc/src)
target_link_libraries(extern_draco PUBLIC dracoenc)

View File

@ -92,6 +92,11 @@ bool AttributeQuantizationTransform::ComputeParameters(
range_ = dif;
}
// In case all values are the same, initialize the range to unit length. This
// will ensure that all values are quantized properly to the same value.
if (range_ == 0.f)
range_ = 1.f;
return true;
}

View File

@ -28,11 +28,11 @@ DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, AttributeValueIndex)
// Index of a point in a PointCloud.
DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, PointIndex)
// Vertex index in a Mesh or CornerTable.
DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, VertexIndex);
DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, VertexIndex)
// Corner index that identifies a corner in a Mesh or CornerTable.
DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, CornerIndex);
DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, CornerIndex)
// Face index for Mesh and CornerTable.
DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, FaceIndex);
DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, FaceIndex)
// Constants denoting invalid indices.
static constexpr AttributeValueIndex kInvalidAttributeValueIndex(

View File

@ -64,7 +64,7 @@ bool PointAttribute::Reset(size_t num_attribute_values) {
return true;
}
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
AttributeValueIndex::ValueType PointAttribute::DeduplicateValues(
const GeometryAttribute &in_att) {
return DeduplicateValues(in_att, AttributeValueIndex(0));

View File

@ -105,7 +105,7 @@ class PointAttribute : public GeometryAttribute {
return GetValue(mapped_index(point_index), out_data);
}
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
// Deduplicate |in_att| values into |this| attribute. |in_att| can be equal
// to |this|.
// Returns -1 if the deduplication failed.
@ -130,7 +130,7 @@ class PointAttribute : public GeometryAttribute {
}
private:
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
template <typename T>
AttributeValueIndex::ValueType DeduplicateTypedValues(
const GeometryAttribute &in_att, AttributeValueIndex in_att_offset);

View File

@ -42,7 +42,7 @@ class PseudoPointD {
// Specifically copies referenced memory
void swap(PseudoPointD &other) noexcept {
for (auto dim = 0; dim < dimension_; dim += 1)
for (internal_t dim = 0; dim < dimension_; dim += 1)
std::swap(mem_[dim], other.mem_[dim]);
}

View File

@ -21,7 +21,9 @@
#include "draco/draco_features.h"
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h"
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h"
#endif
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h"
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_decoder.h"
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h"
@ -82,16 +84,20 @@ struct MeshPredictionSchemeDecoderFactory {
new MeshPredictionSchemeTexCoordsPortableDecoder<
DataTypeT, TransformT, MeshDataT>(attribute, transform,
mesh_data));
} else if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
}
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
else if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
new MeshPredictionSchemeGeometricNormalDecoder<
DataTypeT, TransformT, MeshDataT>(attribute, transform,
mesh_data));
}
#endif
return nullptr;
}
};
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
// Operator () specialized for normal octahedron transforms. These transforms
// are currently used only by the geometric normal prediction scheme (the
// transform is also used by delta coding, but delta predictor is not
@ -128,6 +134,7 @@ struct MeshPredictionSchemeDecoderFactory {
return nullptr;
}
};
#endif
template <class TransformT, class MeshDataT>
std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>> operator()(

View File

@ -32,10 +32,12 @@ PredictionSchemeMethod SelectPredictionMethod(
}
}
if (att->attribute_type() == GeometryAttribute::NORMAL) {
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
if (encoder->options()->GetSpeed() < 4) {
// Use geometric normal prediction for speeds 0, 1, 2, 3.
return MESH_PREDICTION_GEOMETRIC_NORMAL;
}
#endif
return PREDICTION_DIFFERENCE; // default
}
// Handle other attribute types.

View File

@ -19,7 +19,9 @@
#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODER_FACTORY_H_
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h"
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h"
#endif
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h"
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h"
#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h"
@ -49,32 +51,25 @@ struct MeshPredictionSchemeEncoderFactory {
new MeshPredictionSchemeParallelogramEncoder<DataTypeT, TransformT,
MeshDataT>(
attribute, transform, mesh_data));
} else if (method == MESH_PREDICTION_MULTI_PARALLELOGRAM) {
return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
new MeshPredictionSchemeMultiParallelogramEncoder<
DataTypeT, TransformT, MeshDataT>(attribute, transform,
mesh_data));
} else if (method == MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM) {
return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
new MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
DataTypeT, TransformT, MeshDataT>(attribute, transform,
mesh_data));
} else if (method == MESH_PREDICTION_TEX_COORDS_DEPRECATED) {
return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
new MeshPredictionSchemeTexCoordsEncoder<DataTypeT, TransformT,
MeshDataT>(
attribute, transform, mesh_data));
} else if (method == MESH_PREDICTION_TEX_COORDS_PORTABLE) {
return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
new MeshPredictionSchemeTexCoordsPortableEncoder<
DataTypeT, TransformT, MeshDataT>(attribute, transform,
mesh_data));
} else if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
}
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
else if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
new MeshPredictionSchemeGeometricNormalEncoder<DataTypeT, TransformT,
MeshDataT>(
attribute, transform, mesh_data));
}
#endif
return nullptr;
}
};

View File

@ -13,7 +13,9 @@
// limitations under the License.
//
#include "draco/compression/attributes/sequential_attribute_decoders_controller.h"
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
#include "draco/compression/attributes/sequential_normal_attribute_decoder.h"
#endif
#include "draco/compression/attributes/sequential_quantization_attribute_decoder.h"
#include "draco/compression/config/compression_shared.h"
@ -123,9 +125,11 @@ SequentialAttributeDecodersController::CreateSequentialDecoder(
case SEQUENTIAL_ATTRIBUTE_ENCODER_QUANTIZATION:
return std::unique_ptr<SequentialAttributeDecoder>(
new SequentialQuantizationAttributeDecoder());
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
case SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS:
return std::unique_ptr<SequentialNormalAttributeDecoder>(
new SequentialNormalAttributeDecoder());
#endif
default:
break;
}

View File

@ -13,7 +13,9 @@
// limitations under the License.
//
#include "draco/compression/attributes/sequential_attribute_encoders_controller.h"
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
#include "draco/compression/attributes/sequential_normal_attribute_encoder.h"
#endif
#include "draco/compression/attributes/sequential_quantization_attribute_encoder.h"
#include "draco/compression/point_cloud/point_cloud_encoder.h"
@ -121,15 +123,19 @@ SequentialAttributeEncodersController::CreateSequentialEncoder(int i) {
case DT_FLOAT32:
if (encoder()->options()->GetAttributeInt(att_id, "quantization_bits",
-1) > 0) {
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
if (att->attribute_type() == GeometryAttribute::NORMAL) {
// We currently only support normals with float coordinates
// and must be quantized.
return std::unique_ptr<SequentialAttributeEncoder>(
new SequentialNormalAttributeEncoder());
} else {
#endif
return std::unique_ptr<SequentialAttributeEncoder>(
new SequentialQuantizationAttributeEncoder());
#ifdef DRACO_NORMAL_ENCODING_SUPPORTED
}
#endif
}
break;
default:

View File

@ -37,7 +37,7 @@ StatusOr<std::unique_ptr<PointCloudDecoder>> CreatePointCloudDecoder(
} else if (method == POINT_CLOUD_KD_TREE_ENCODING) {
return std::unique_ptr<PointCloudDecoder>(new PointCloudKdTreeDecoder());
}
return Status(Status::ERROR, "Unsupported encoding method.");
return Status(Status::DRACO_ERROR, "Unsupported encoding method.");
}
#endif
@ -48,7 +48,7 @@ StatusOr<std::unique_ptr<MeshDecoder>> CreateMeshDecoder(uint8_t method) {
} else if (method == MESH_EDGEBREAKER_ENCODING) {
return std::unique_ptr<MeshDecoder>(new MeshEdgebreakerDecoder());
}
return Status(Status::ERROR, "Unsupported encoding method.");
return Status(Status::DRACO_ERROR, "Unsupported encoding method.");
}
#endif
@ -77,7 +77,7 @@ StatusOr<std::unique_ptr<PointCloud>> Decoder::DecodePointCloudFromBuffer(
return static_cast<std::unique_ptr<PointCloud>>(std::move(mesh));
#endif
}
return Status(Status::ERROR, "Unsupported geometry type.");
return Status(Status::DRACO_ERROR, "Unsupported geometry type.");
}
StatusOr<std::unique_ptr<Mesh>> Decoder::DecodeMeshFromBuffer(
@ -94,7 +94,7 @@ Status Decoder::DecodeBufferToGeometry(DecoderBuffer *in_buffer,
DracoHeader header;
DRACO_RETURN_IF_ERROR(PointCloudDecoder::DecodeHeader(&temp_buffer, &header))
if (header.encoder_type != POINT_CLOUD) {
return Status(Status::ERROR, "Input is not a point cloud.");
return Status(Status::DRACO_ERROR, "Input is not a point cloud.");
}
DRACO_ASSIGN_OR_RETURN(std::unique_ptr<PointCloudDecoder> decoder,
CreatePointCloudDecoder(header.encoder_method))
@ -102,7 +102,7 @@ Status Decoder::DecodeBufferToGeometry(DecoderBuffer *in_buffer,
DRACO_RETURN_IF_ERROR(decoder->Decode(options_, in_buffer, out_geometry))
return OkStatus();
#else
return Status(Status::ERROR, "Unsupported geometry type.");
return Status(Status::DRACO_ERROR, "Unsupported geometry type.");
#endif
}
@ -113,7 +113,7 @@ Status Decoder::DecodeBufferToGeometry(DecoderBuffer *in_buffer,
DracoHeader header;
DRACO_RETURN_IF_ERROR(PointCloudDecoder::DecodeHeader(&temp_buffer, &header))
if (header.encoder_type != TRIANGULAR_MESH) {
return Status(Status::ERROR, "Input is not a mesh.");
return Status(Status::DRACO_ERROR, "Input is not a mesh.");
}
DRACO_ASSIGN_OR_RETURN(std::unique_ptr<MeshDecoder> decoder,
CreateMeshDecoder(header.encoder_method))
@ -121,7 +121,7 @@ Status Decoder::DecodeBufferToGeometry(DecoderBuffer *in_buffer,
DRACO_RETURN_IF_ERROR(decoder->Decode(options_, in_buffer, out_geometry))
return OkStatus();
#else
return Status(Status::ERROR, "Unsupported geometry type.");
return Status(Status::DRACO_ERROR, "Unsupported geometry type.");
#endif
}

View File

@ -20,7 +20,7 @@
#include "draco/compression/config/compression_shared.h"
#include "draco/compression/config/decoder_options.h"
#include "draco/core/decoder_buffer.h"
#include "draco/core/statusor.h"
#include "draco/core/status_or.h"
#include "draco/mesh/mesh.h"
namespace draco {

View File

@ -68,23 +68,28 @@ class EncoderBase {
Status CheckPredictionScheme(GeometryAttribute::Type att_type,
int prediction_scheme) const {
// Out of bound checks:
if (prediction_scheme < 0)
return Status(Status::ERROR, "Invalid prediction scheme requested.");
if (prediction_scheme < PREDICTION_NONE)
return Status(Status::DRACO_ERROR,
"Invalid prediction scheme requested.");
if (prediction_scheme >= NUM_PREDICTION_SCHEMES)
return Status(Status::ERROR, "Invalid prediction scheme requested.");
return Status(Status::DRACO_ERROR,
"Invalid prediction scheme requested.");
// Deprecated prediction schemes:
if (prediction_scheme == MESH_PREDICTION_TEX_COORDS_DEPRECATED)
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"MESH_PREDICTION_TEX_COORDS_DEPRECATED is deprecated.");
if (prediction_scheme == MESH_PREDICTION_MULTI_PARALLELOGRAM)
return Status(Status::DRACO_ERROR,
"MESH_PREDICTION_MULTI_PARALLELOGRAM is deprecated.");
// Attribute specific checks:
if (prediction_scheme == MESH_PREDICTION_TEX_COORDS_PORTABLE) {
if (att_type != GeometryAttribute::TEX_COORD)
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"Invalid prediction scheme for attribute type.");
}
if (prediction_scheme == MESH_PREDICTION_GEOMETRIC_NORMAL) {
if (att_type != GeometryAttribute::NORMAL) {
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"Invalid prediction scheme for attribute type.");
}
}
@ -92,7 +97,7 @@ class EncoderBase {
if (att_type == GeometryAttribute::NORMAL) {
if (!(prediction_scheme == PREDICTION_DIFFERENCE ||
prediction_scheme == MESH_PREDICTION_GEOMETRIC_NORMAL)) {
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"Invalid prediction scheme for attribute type.");
}
}

View File

@ -20,29 +20,29 @@
#include <vector>
#define ANS_DIVIDE_BY_MULTIPLY 1
#if ANS_DIVIDE_BY_MULTIPLY
#define DRACO_ANS_DIVIDE_BY_MULTIPLY 1
#if DRACO_ANS_DIVIDE_BY_MULTIPLY
#include "draco/core/divide.h"
#endif
#include "draco/core/macros.h"
namespace draco {
#if ANS_DIVIDE_BY_MULTIPLY
#if DRACO_ANS_DIVIDE_BY_MULTIPLY
#define ANS_DIVREM(quotient, remainder, dividend, divisor) \
do { \
quotient = fastdiv(dividend, divisor); \
remainder = dividend - quotient * divisor; \
#define DRACO_ANS_DIVREM(quotient, remainder, dividend, divisor) \
do { \
quotient = fastdiv(dividend, divisor); \
remainder = dividend - quotient * divisor; \
} while (0)
#define ANS_DIV(dividend, divisor) fastdiv(dividend, divisor)
#define DRACO_ANS_DIV(dividend, divisor) fastdiv(dividend, divisor)
#else
#define ANS_DIVREM(quotient, remainder, dividend, divisor) \
do { \
quotient = dividend / divisor; \
remainder = dividend % divisor; \
#define DRACO_ANS_DIVREM(quotient, remainder, dividend, divisor) \
do { \
quotient = dividend / divisor; \
remainder = dividend % divisor; \
} while (0)
#define ANS_DIV(dividend, divisor) ((dividend) / (divisor))
#define DRACO_ANS_DIV(dividend, divisor) ((dividend) / (divisor))
#endif
struct AnsCoder {
@ -60,13 +60,9 @@ struct AnsDecoder {
};
typedef uint8_t AnsP8;
#define ans_p8_precision 256u
#define ans_p8_shift 8
#define ans_p10_precision 1024u
#define l_base (ans_p10_precision * 4) // l_base % precision must be 0
#define io_base 256
// Range I = { l_base, l_base + 1, ..., l_base * io_base - 1 }
#define DRACO_ANS_P8_PRECISION 256u
#define DRACO_ANS_L_BASE (4096u)
#define DRACO_ANS_IO_BASE 256
static uint32_t mem_get_le16(const void *vmem) {
uint32_t val;
@ -126,14 +122,14 @@ static inline void ans_write_init(struct AnsCoder *const ans,
uint8_t *const buf) {
ans->buf = buf;
ans->buf_offset = 0;
ans->state = l_base;
ans->state = DRACO_ANS_L_BASE;
}
static inline int ans_write_end(struct AnsCoder *const ans) {
uint32_t state;
DRACO_DCHECK_GE(ans->state, l_base);
DRACO_DCHECK_LT(ans->state, l_base * io_base);
state = ans->state - l_base;
DRACO_DCHECK_GE(ans->state, DRACO_ANS_L_BASE);
DRACO_DCHECK_LT(ans->state, DRACO_ANS_L_BASE * DRACO_ANS_IO_BASE);
state = ans->state - DRACO_ANS_L_BASE;
if (state < (1 << 6)) {
ans->buf[ans->buf_offset] = (0x00 << 6) + state;
return ans->buf_offset + 1;
@ -149,43 +145,44 @@ static inline int ans_write_end(struct AnsCoder *const ans) {
}
}
// rABS with descending spread
// p or p0 takes the place of l_s from the paper
// ans_p8_precision is m
// rABS with descending spread.
// p or p0 takes the place of l_s from the paper.
// DRACO_ANS_P8_PRECISION is m.
static inline void rabs_desc_write(struct AnsCoder *ans, int val, AnsP8 p0) {
const AnsP8 p = ans_p8_precision - p0;
const AnsP8 p = DRACO_ANS_P8_PRECISION - p0;
const unsigned l_s = val ? p : p0;
unsigned quot, rem;
if (ans->state >= l_base / ans_p8_precision * io_base * l_s) {
ans->buf[ans->buf_offset++] = ans->state % io_base;
ans->state /= io_base;
if (ans->state >=
DRACO_ANS_L_BASE / DRACO_ANS_P8_PRECISION * DRACO_ANS_IO_BASE * l_s) {
ans->buf[ans->buf_offset++] = ans->state % DRACO_ANS_IO_BASE;
ans->state /= DRACO_ANS_IO_BASE;
}
ANS_DIVREM(quot, rem, ans->state, l_s);
ans->state = quot * ans_p8_precision + rem + (val ? 0 : p);
DRACO_ANS_DIVREM(quot, rem, ans->state, l_s);
ans->state = quot * DRACO_ANS_P8_PRECISION + rem + (val ? 0 : p);
}
#define ANS_IMPL1 0
#define DRACO_ANS_IMPL1 0
#define UNPREDICTABLE(x) x
static inline int rabs_desc_read(struct AnsDecoder *ans, AnsP8 p0) {
int val;
#if ANS_IMPL1
#if DRACO_ANS_IMPL1
unsigned l_s;
#else
unsigned quot, rem, x, xn;
#endif
const AnsP8 p = ans_p8_precision - p0;
if (ans->state < l_base && ans->buf_offset > 0) {
ans->state = ans->state * io_base + ans->buf[--ans->buf_offset];
const AnsP8 p = DRACO_ANS_P8_PRECISION - p0;
if (ans->state < DRACO_ANS_L_BASE && ans->buf_offset > 0) {
ans->state = ans->state * DRACO_ANS_IO_BASE + ans->buf[--ans->buf_offset];
}
#if ANS_IMPL1
val = ans->state % ans_p8_precision < p;
#if DRACO_ANS_IMPL1
val = ans->state % DRACO_ANS_P8_PRECISION < p;
l_s = val ? p : p0;
ans->state = (ans->state / ans_p8_precision) * l_s +
ans->state % ans_p8_precision - (!val * p);
ans->state = (ans->state / DRACO_ANS_P8_PRECISION) * l_s +
ans->state % DRACO_ANS_P8_PRECISION - (!val * p);
#else
x = ans->state;
quot = x / ans_p8_precision;
rem = x % ans_p8_precision;
quot = x / DRACO_ANS_P8_PRECISION;
rem = x % DRACO_ANS_P8_PRECISION;
xn = quot * p;
val = rem < p;
if (UNPREDICTABLE(val)) {
@ -198,41 +195,42 @@ static inline int rabs_desc_read(struct AnsDecoder *ans, AnsP8 p0) {
return val;
}
// rABS with ascending spread
// p or p0 takes the place of l_s from the paper
// ans_p8_precision is m
// rABS with ascending spread.
// p or p0 takes the place of l_s from the paper.
// DRACO_ANS_P8_PRECISION is m.
static inline void rabs_asc_write(struct AnsCoder *ans, int val, AnsP8 p0) {
const AnsP8 p = ans_p8_precision - p0;
const AnsP8 p = DRACO_ANS_P8_PRECISION - p0;
const unsigned l_s = val ? p : p0;
unsigned quot, rem;
if (ans->state >= l_base / ans_p8_precision * io_base * l_s) {
ans->buf[ans->buf_offset++] = ans->state % io_base;
ans->state /= io_base;
if (ans->state >=
DRACO_ANS_L_BASE / DRACO_ANS_P8_PRECISION * DRACO_ANS_IO_BASE * l_s) {
ans->buf[ans->buf_offset++] = ans->state % DRACO_ANS_IO_BASE;
ans->state /= DRACO_ANS_IO_BASE;
}
ANS_DIVREM(quot, rem, ans->state, l_s);
ans->state = quot * ans_p8_precision + rem + (val ? p0 : 0);
DRACO_ANS_DIVREM(quot, rem, ans->state, l_s);
ans->state = quot * DRACO_ANS_P8_PRECISION + rem + (val ? p0 : 0);
}
static inline int rabs_asc_read(struct AnsDecoder *ans, AnsP8 p0) {
int val;
#if ANS_IMPL1
#if DRACO_ANS_IMPL1
unsigned l_s;
#else
unsigned quot, rem, x, xn;
#endif
const AnsP8 p = ans_p8_precision - p0;
if (ans->state < l_base) {
ans->state = ans->state * io_base + ans->buf[--ans->buf_offset];
const AnsP8 p = DRACO_ANS_P8_PRECISION - p0;
if (ans->state < DRACO_ANS_L_BASE) {
ans->state = ans->state * DRACO_ANS_IO_BASE + ans->buf[--ans->buf_offset];
}
#if ANS_IMPL1
val = ans->state % ans_p8_precision < p;
#if DRACO_ANS_IMPL1
val = ans->state % DRACO_ANS_P8_PRECISION < p;
l_s = val ? p : p0;
ans->state = (ans->state / ans_p8_precision) * l_s +
ans->state % ans_p8_precision - (!val * p);
ans->state = (ans->state / DRACO_ANS_P8_PRECISION) * l_s +
ans->state % DRACO_ANS_P8_PRECISION - (!val * p);
#else
x = ans->state;
quot = x / ans_p8_precision;
rem = x % ans_p8_precision;
quot = x / DRACO_ANS_P8_PRECISION;
rem = x % DRACO_ANS_P8_PRECISION;
xn = quot * p;
val = rem >= p0;
if (UNPREDICTABLE(val)) {
@ -248,32 +246,34 @@ static inline int rabs_asc_read(struct AnsDecoder *ans, AnsP8 p0) {
#define rabs_read rabs_desc_read
#define rabs_write rabs_desc_write
// uABS with normalization
// uABS with normalization.
static inline void uabs_write(struct AnsCoder *ans, int val, AnsP8 p0) {
AnsP8 p = ans_p8_precision - p0;
AnsP8 p = DRACO_ANS_P8_PRECISION - p0;
const unsigned l_s = val ? p : p0;
while (ans->state >= l_base / ans_p8_precision * io_base * l_s) {
ans->buf[ans->buf_offset++] = ans->state % io_base;
ans->state /= io_base;
while (ans->state >=
DRACO_ANS_L_BASE / DRACO_ANS_P8_PRECISION * DRACO_ANS_IO_BASE * l_s) {
ans->buf[ans->buf_offset++] = ans->state % DRACO_ANS_IO_BASE;
ans->state /= DRACO_ANS_IO_BASE;
}
if (!val)
ans->state = ANS_DIV(ans->state * ans_p8_precision, p0);
ans->state = DRACO_ANS_DIV(ans->state * DRACO_ANS_P8_PRECISION, p0);
else
ans->state = ANS_DIV((ans->state + 1) * ans_p8_precision + p - 1, p) - 1;
ans->state =
DRACO_ANS_DIV((ans->state + 1) * DRACO_ANS_P8_PRECISION + p - 1, p) - 1;
}
static inline int uabs_read(struct AnsDecoder *ans, AnsP8 p0) {
AnsP8 p = ans_p8_precision - p0;
AnsP8 p = DRACO_ANS_P8_PRECISION - p0;
int s;
// unsigned int xp1;
unsigned xp, sp;
unsigned state = ans->state;
while (state < l_base && ans->buf_offset > 0) {
state = state * io_base + ans->buf[--ans->buf_offset];
while (state < DRACO_ANS_L_BASE && ans->buf_offset > 0) {
state = state * DRACO_ANS_IO_BASE + ans->buf[--ans->buf_offset];
}
sp = state * p;
// xp1 = (sp + p) / ans_p8_precision;
xp = sp / ans_p8_precision;
// xp1 = (sp + p) / DRACO_ANS_P8_PRECISION;
xp = sp / DRACO_ANS_P8_PRECISION;
// s = xp1 - xp;
s = (sp & 0xFF) >= p0;
if (UNPREDICTABLE(s))
@ -286,8 +286,8 @@ static inline int uabs_read(struct AnsDecoder *ans, AnsP8 p0) {
static inline int uabs_read_bit(struct AnsDecoder *ans) {
int s;
unsigned state = ans->state;
while (state < l_base && ans->buf_offset > 0) {
state = state * io_base + ans->buf[--ans->buf_offset];
while (state < DRACO_ANS_L_BASE && ans->buf_offset > 0) {
state = state * DRACO_ANS_IO_BASE + ans->buf[--ans->buf_offset];
}
s = static_cast<int>(state & 1);
ans->state = state >> 1;
@ -317,23 +317,23 @@ static inline int ans_read_init(struct AnsDecoder *const ans,
} else {
return 1;
}
ans->state += l_base;
if (ans->state >= l_base * io_base)
ans->state += DRACO_ANS_L_BASE;
if (ans->state >= DRACO_ANS_L_BASE * DRACO_ANS_IO_BASE)
return 1;
return 0;
}
static inline int ans_read_end(struct AnsDecoder *const ans) {
return ans->state == l_base;
return ans->state == DRACO_ANS_L_BASE;
}
static inline int ans_reader_has_error(const struct AnsDecoder *const ans) {
return ans->state < l_base && ans->buf_offset == 0;
return ans->state < DRACO_ANS_L_BASE && ans->buf_offset == 0;
}
struct rans_sym {
uint32_t prob;
uint32_t cum_prob; // not-inclusive
uint32_t cum_prob; // not-inclusive.
};
// Class for performing rANS encoding using a desired number of precision bits.
@ -356,7 +356,7 @@ class RAnsEncoder {
inline int write_end() {
uint32_t state;
DRACO_DCHECK_GE(ans_.state, l_rans_base);
DRACO_DCHECK_LT(ans_.state, l_rans_base * io_base);
DRACO_DCHECK_LT(ans_.state, l_rans_base * DRACO_ANS_IO_BASE);
state = ans_.state - l_rans_base;
if (state < (1 << 6)) {
ans_.buf[ans_.buf_offset] = (0x00 << 6) + state;
@ -376,14 +376,14 @@ class RAnsEncoder {
}
}
// rANS with normalization
// sym->prob takes the place of l_s from the paper
// rans_precision is m
// rANS with normalization.
// sym->prob takes the place of l_s from the paper.
// rans_precision is m.
inline void rans_write(const struct rans_sym *const sym) {
const uint32_t p = sym->prob;
while (ans_.state >= l_rans_base / rans_precision * io_base * p) {
ans_.buf[ans_.buf_offset++] = ans_.state % io_base;
ans_.state /= io_base;
while (ans_.state >= l_rans_base / rans_precision * DRACO_ANS_IO_BASE * p) {
ans_.buf[ans_.buf_offset++] = ans_.state % DRACO_ANS_IO_BASE;
ans_.state /= DRACO_ANS_IO_BASE;
}
// TODO(ostava): The division and multiplication should be optimized.
ans_.state =
@ -399,7 +399,7 @@ class RAnsEncoder {
struct rans_dec_sym {
uint32_t val;
uint32_t prob;
uint32_t cum_prob; // not-inclusive
uint32_t cum_prob; // not-inclusive.
};
// Class for performing rANS decoding using a desired number of precision bits.
@ -439,7 +439,7 @@ class RAnsDecoder {
return 1;
}
ans_.state += l_rans_base;
if (ans_.state >= l_rans_base * io_base)
if (ans_.state >= l_rans_base * DRACO_ANS_IO_BASE)
return 1;
return 0;
}
@ -455,7 +455,7 @@ class RAnsDecoder {
unsigned quo;
struct rans_dec_sym sym;
while (ans_.state < l_rans_base && ans_.buf_offset > 0) {
ans_.state = ans_.state * io_base + ans_.buf[--ans_.buf_offset];
ans_.state = ans_.state * DRACO_ANS_IO_BASE + ans_.buf[--ans_.buf_offset];
}
// |rans_precision| is a power of two compile time constant, and the below
// division and modulo are going to be optimized by the compiler.
@ -507,7 +507,10 @@ class RAnsDecoder {
AnsDecoder ans_;
};
#undef ANS_DIVREM
#undef DRACO_ANS_DIVREM
#undef DRACO_ANS_P8_PRECISION
#undef DRACO_ANS_L_BASE
#undef DRACO_ANS_IO_BASE
} // namespace draco

View File

@ -40,8 +40,8 @@ constexpr int ComputeRAnsPrecisionFromUniqueSymbolsBitLength(
// Compute approximate frequency table size needed for storing the provided
// symbols.
static int64_t ApproximateRAnsFrequencyTableBits(int32_t max_value,
int num_unique_symbols) {
static inline int64_t ApproximateRAnsFrequencyTableBits(
int32_t max_value, int num_unique_symbols) {
// Approximate number of bits for storing zero frequency entries using the
// run length encoding (with max length of 64).
const int64_t table_zero_frequency_bits =

View File

@ -16,8 +16,10 @@
#include "draco/compression/mesh/mesh_edgebreaker_encoder.h"
#include "draco/compression/mesh/mesh_sequential_encoder.h"
#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED
#include "draco/compression/point_cloud/point_cloud_kd_tree_encoder.h"
#include "draco/compression/point_cloud/point_cloud_sequential_encoder.h"
#endif
namespace draco {
@ -29,7 +31,7 @@ ExpertEncoder::ExpertEncoder(const Mesh &mesh)
Status ExpertEncoder::EncodeToBuffer(EncoderBuffer *out_buffer) {
if (point_cloud_ == nullptr)
return Status(Status::ERROR, "Invalid input geometry.");
return Status(Status::DRACO_ERROR, "Invalid input geometry.");
if (mesh_ == nullptr) {
return EncodePointCloudToBuffer(*point_cloud_, out_buffer);
}
@ -38,6 +40,7 @@ Status ExpertEncoder::EncodeToBuffer(EncoderBuffer *out_buffer) {
Status ExpertEncoder::EncodePointCloudToBuffer(const PointCloud &pc,
EncoderBuffer *out_buffer) {
#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED
std::unique_ptr<PointCloudEncoder> encoder;
const int encoding_method = options().GetGlobalInt("encoding_method", -1);
@ -74,7 +77,7 @@ Status ExpertEncoder::EncodePointCloudToBuffer(const PointCloud &pc,
} else if (encoding_method == POINT_CLOUD_KD_TREE_ENCODING) {
// Encoding method was explicitly specified but we cannot use it for
// the given input (some of the checks above failed).
return Status(Status::ERROR, "Invalid encoding method.");
return Status(Status::DRACO_ERROR, "Invalid encoding method.");
}
}
if (!encoder) {
@ -87,6 +90,9 @@ Status ExpertEncoder::EncodePointCloudToBuffer(const PointCloud &pc,
set_num_encoded_points(encoder->num_encoded_points());
set_num_encoded_faces(0);
return OkStatus();
#else
return Status(Status::DRACO_ERROR, "Point cloud encoding is not enabled.");
#endif
}
Status ExpertEncoder::EncodeMeshToBuffer(const Mesh &m,

View File

@ -31,9 +31,7 @@ bool MeshEdgebreakerEncoder::InitializeEncoder() {
impl_ = nullptr;
// For tiny meshes it's usually better to use the basic edgebreaker as the
// overhead of the predictive one may turn out to be too big.
// TODO(ostava): For now we have a set limit for forcing the basic edgebreaker
// based on the number of faces, but a more complex heuristic may be used if
// needed.
// TODO(b/111065939): Check if this can be improved.
const bool is_tiny_mesh = mesh()->num_faces() < 1000;
int selected_edgebreaker_method =
@ -81,7 +79,7 @@ bool MeshEdgebreakerEncoder::EncodeAttributesEncoderIdentifier(
return true;
}
bool MeshEdgebreakerEncoder::EncodeConnectivity() {
Status MeshEdgebreakerEncoder::EncodeConnectivity() {
return impl_->EncodeConnectivity();
}

View File

@ -51,7 +51,7 @@ class MeshEdgebreakerEncoder : public MeshEncoder {
protected:
bool InitializeEncoder() override;
bool EncodeConnectivity() override;
Status EncodeConnectivity() override;
bool GenerateAttributesEncoder(int32_t att_id) override;
bool EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) override;
void ComputeNumberOfEncodedPoints() override;

View File

@ -263,7 +263,7 @@ bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::
}
template <class TraversalEncoder>
bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
Status MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
// To encode the mesh, we need face connectivity data stored in a corner
// table. To compute the connectivity we must use indices associated with
// POSITION attribute, because they define which edges can be connected
@ -279,7 +279,7 @@ bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
corner_table_->num_faces() == corner_table_->NumDegeneratedFaces()) {
// Failed to construct the corner table.
// TODO(ostava): Add better error reporting.
return false;
return Status(Status::DRACO_ERROR, "All triangles are degenerate.");
}
traversal_encoder_.Init(this);
@ -317,10 +317,10 @@ bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
pos_encoding_data_.num_values = 0;
if (!FindHoles())
return false;
return Status(Status::DRACO_ERROR, "Failed to process mesh holes.");
if (!InitAttributeData())
return false;
return Status(Status::DRACO_ERROR, "Failed to initialize attribute data.");
const uint8_t num_attribute_data =
static_cast<uint8_t>(attribute_data_.size());
@ -376,7 +376,7 @@ bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
if (opp_face_id != kInvalidFaceIndex &&
!visited_faces_[opp_face_id.value()]) {
if (!EncodeConnectivityFromCorner(opp_id))
return false;
return Status(Status::DRACO_ERROR, "Failed to encode mesh component.");
}
} else {
// Boundary configuration. We start on a boundary rather than on a face.
@ -385,7 +385,7 @@ bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
// Start processing the face opposite to the boundary edge (the face
// containing the start_corner).
if (!EncodeConnectivityFromCorner(start_corner))
return false;
return Status(Status::DRACO_ERROR, "Failed to encode mesh component.");
}
}
// Reverse the order of connectivity corners to match the order in which
@ -417,11 +417,11 @@ bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
// Append the traversal buffer.
if (!EncodeSplitData())
return false;
return Status(Status::DRACO_ERROR, "Failed to encode split data.");
encoder_->buffer()->Encode(traversal_encoder_.buffer().data(),
traversal_encoder_.buffer().size());
return true;
return OkStatus();
}
template <class TraversalEncoder>

View File

@ -45,7 +45,7 @@ class MeshEdgebreakerEncoderImpl : public MeshEdgebreakerEncoderImplInterface {
bool GenerateAttributesEncoder(int32_t att_id) override;
bool EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) override;
bool EncodeConnectivity() override;
Status EncodeConnectivity() override;
const CornerTable *GetCornerTable() const override {
return corner_table_.get();

View File

@ -41,7 +41,7 @@ class MeshEdgebreakerEncoderImplInterface {
int att_id) const = 0;
virtual bool GenerateAttributesEncoder(int32_t att_id) = 0;
virtual bool EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) = 0;
virtual bool EncodeConnectivity() = 0;
virtual Status EncodeConnectivity() = 0;
// Returns corner table of the encoded mesh.
virtual const CornerTable *GetCornerTable() const = 0;

View File

@ -23,12 +23,11 @@ void MeshEncoder::SetMesh(const Mesh &m) {
SetPointCloud(m);
}
bool MeshEncoder::EncodeGeometryData() {
if (!EncodeConnectivity())
return false;
Status MeshEncoder::EncodeGeometryData() {
DRACO_RETURN_IF_ERROR(EncodeConnectivity());
if (options()->GetGlobalBool("store_number_of_encoded_faces", false))
ComputeNumberOfEncodedFaces();
return true;
return OkStatus();
}
} // namespace draco

View File

@ -61,10 +61,10 @@ class MeshEncoder : public PointCloudEncoder {
const Mesh *mesh() const { return mesh_; }
protected:
bool EncodeGeometryData() override;
Status EncodeGeometryData() override;
// Needs to be implemented by the derived classes.
virtual bool EncodeConnectivity() = 0;
virtual Status EncodeConnectivity() = 0;
// Computes and sets the num_encoded_faces_ for the encoder.
virtual void ComputeNumberOfEncodedFaces() = 0;

View File

@ -87,7 +87,7 @@ TEST_P(MeshEncoderTest, EncodeGoldenMesh) {
}
}
INSTANTIATE_TEST_CASE_P(MeshEncoderTests, MeshEncoderTest,
::testing::Values("sequential", "edgebreaker"));
INSTANTIATE_TEST_SUITE_P(MeshEncoderTests, MeshEncoderTest,
::testing::Values("sequential", "edgebreaker"));
} // namespace draco

View File

@ -25,7 +25,7 @@ namespace draco {
MeshSequentialEncoder::MeshSequentialEncoder() {}
bool MeshSequentialEncoder::EncodeConnectivity() {
Status MeshSequentialEncoder::EncodeConnectivity() {
// Serialize indices.
const uint32_t num_faces = mesh()->num_faces();
EncodeVarint(num_faces, buffer());
@ -38,7 +38,7 @@ bool MeshSequentialEncoder::EncodeConnectivity() {
// 0 = Encode compressed indices.
buffer()->Encode(static_cast<uint8_t>(0));
if (!CompressAndEncodeIndices())
return false;
return Status(Status::DRACO_ERROR, "Failed to compress connectivity.");
} else {
// 1 = Encode indices directly.
buffer()->Encode(static_cast<uint8_t>(1));
@ -77,7 +77,7 @@ bool MeshSequentialEncoder::EncodeConnectivity() {
}
}
}
return true;
return OkStatus();
}
bool MeshSequentialEncoder::GenerateAttributesEncoder(int32_t att_id) {

View File

@ -42,7 +42,7 @@ class MeshSequentialEncoder : public MeshEncoder {
}
protected:
bool EncodeConnectivity() override;
Status EncodeConnectivity() override;
bool GenerateAttributesEncoder(int32_t att_id) override;
void ComputeNumberOfEncodedPoints() override;
void ComputeNumberOfEncodedFaces() override;

View File

@ -95,8 +95,11 @@ class DynamicIntegerPointsKdTreeDecoder {
// Decodes a integer point cloud from |buffer|.
template <class OutputIteratorT>
bool DecodePoints(DecoderBuffer *buffer, OutputIteratorT &oit);
#ifndef DRACO_OLD_GCC
template <class OutputIteratorT>
bool DecodePoints(DecoderBuffer *buffer, OutputIteratorT &&oit);
#endif // DRACO_OLD_GCC
const uint32_t dimension() const { return dimension_; }
@ -138,6 +141,7 @@ class DynamicIntegerPointsKdTreeDecoder {
};
// Decodes a point cloud from |buffer|.
#ifndef DRACO_OLD_GCC
template <int compression_level_t>
template <class OutputIteratorT>
bool DynamicIntegerPointsKdTreeDecoder<compression_level_t>::DecodePoints(
@ -145,6 +149,7 @@ bool DynamicIntegerPointsKdTreeDecoder<compression_level_t>::DecodePoints(
OutputIteratorT local = std::forward<OutputIteratorT>(oit);
return DecodePoints(buffer, local);
}
#endif // DRACO_OLD_GCC
template <int compression_level_t>
template <class OutputIteratorT>

View File

@ -33,8 +33,12 @@ class FloatPointsTreeDecoder {
// Decodes a point cloud from |buffer|.
template <class OutputIteratorT>
bool DecodePointCloud(DecoderBuffer *buffer, OutputIteratorT &out);
#ifndef DRACO_OLD_GCC
template <class OutputIteratorT>
bool DecodePointCloud(DecoderBuffer *buffer, OutputIteratorT &&out);
#endif // DRACO_OLD_GCC
// Initializes a DecoderBuffer from |data|, and calls function above.
template <class OutputIteratorT>
bool DecodePointCloud(const char *data, size_t data_size,
@ -72,12 +76,16 @@ class FloatPointsTreeDecoder {
uint32_t compression_level_;
};
#ifndef DRACO_OLD_GCC
// TODO(vytyaz): Reenable once USD migrates from GCC 4.8 to a higher version
// that can disambiguate calls to overloaded methods taking rvalue reference.
template <class OutputIteratorT>
bool FloatPointsTreeDecoder::DecodePointCloud(DecoderBuffer *buffer,
OutputIteratorT &&out) {
OutputIteratorT local = std::forward<OutputIteratorT>(out);
return DecodePointCloud(buffer, local);
}
#endif // DRACO_OLD_GCC
template <class OutputIteratorT>
bool FloatPointsTreeDecoder::DecodePointCloud(DecoderBuffer *buffer,

View File

@ -23,7 +23,7 @@ enum PointCloudCompressionMethod {
// Generalized version of Encoding using the Octree method by Olivier
// Devillers to d dimensions.
// "Progressive lossless compression of arbitrary simplicial complexes"
// http://dx.doi.org/10.1145/566570.566591
// https://doi.org/10.1145/566570.566591
KDTREE = 1,
RESERVED_POINT_CLOUD_METHOD_2 = 2, // Reserved for internal use.
RESERVED_POINT_CLOUD_METHOD_3 = 0, // Reserved for internal use.

View File

@ -31,7 +31,7 @@ Status PointCloudDecoder::DecodeHeader(DecoderBuffer *buffer,
if (!buffer->Decode(out_header->draco_string, 5))
return Status(Status::IO_ERROR, kIoErrorMsg);
if (memcmp(out_header->draco_string, "DRACO", 5) != 0)
return Status(Status::ERROR, "Not a Draco file.");
return Status(Status::DRACO_ERROR, "Not a Draco file.");
if (!buffer->Decode(&(out_header->version_major)))
return Status(Status::IO_ERROR, kIoErrorMsg);
if (!buffer->Decode(&(out_header->version_minor)))
@ -50,7 +50,7 @@ Status PointCloudDecoder::DecodeMetadata() {
std::unique_ptr<GeometryMetadata>(new GeometryMetadata());
MetadataDecoder metadata_decoder;
if (!metadata_decoder.DecodeGeometryMetadata(buffer_, metadata.get()))
return Status(Status::ERROR, "Failed to decode metadata.");
return Status(Status::DRACO_ERROR, "Failed to decode metadata.");
point_cloud_->AddMetadata(std::move(metadata));
return OkStatus();
}
@ -66,7 +66,7 @@ Status PointCloudDecoder::Decode(const DecoderOptions &options,
// Sanity check that we are really using the right decoder (mostly for cases
// where the Decode method was called manually outside of our main API.
if (header.encoder_type != GetGeometryType())
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"Using incompatible decoder for the input geometry.");
// TODO(ostava): We should check the method as well, but currently decoders
// don't expose the decoding method id.
@ -93,11 +93,11 @@ Status PointCloudDecoder::Decode(const DecoderOptions &options,
DRACO_RETURN_IF_ERROR(DecodeMetadata())
}
if (!InitializeDecoder())
return Status(Status::ERROR, "Failed to initialize the decoder.");
return Status(Status::DRACO_ERROR, "Failed to initialize the decoder.");
if (!DecodeGeometryData())
return Status(Status::ERROR, "Failed to decode geometry data.");
return Status(Status::DRACO_ERROR, "Failed to decode geometry data.");
if (!DecodePointAttributes())
return Status(Status::ERROR, "Failed to decode point attributes.");
return Status(Status::DRACO_ERROR, "Failed to decode point attributes.");
return OkStatus();
}

View File

@ -36,17 +36,16 @@ Status PointCloudEncoder::Encode(const EncoderOptions &options,
attributes_encoder_ids_order_.clear();
if (!point_cloud_)
return Status(Status::ERROR, "Invalid input geometry.");
return Status(Status::DRACO_ERROR, "Invalid input geometry.");
DRACO_RETURN_IF_ERROR(EncodeHeader())
DRACO_RETURN_IF_ERROR(EncodeMetadata())
if (!InitializeEncoder())
return Status(Status::ERROR, "Failed to initialize encoder.");
return Status(Status::DRACO_ERROR, "Failed to initialize encoder.");
if (!EncodeEncoderData())
return Status(Status::ERROR, "Failed to encode internal data.");
if (!EncodeGeometryData())
return Status(Status::ERROR, "Failed to encode geometry data.");
return Status(Status::DRACO_ERROR, "Failed to encode internal data.");
DRACO_RETURN_IF_ERROR(EncodeGeometryData());
if (!EncodePointAttributes())
return Status(Status::ERROR, "Failed to encode point attributes.");
return Status(Status::DRACO_ERROR, "Failed to encode point attributes.");
if (options.GetGlobalBool("store_number_of_encoded_points", false))
ComputeNumberOfEncodedPoints();
return OkStatus();
@ -87,7 +86,7 @@ Status PointCloudEncoder::EncodeMetadata() {
MetadataEncoder metadata_encoder;
if (!metadata_encoder.EncodeGeometryMetadata(buffer_,
point_cloud_->GetMetadata())) {
return Status(Status::ERROR, "Failed to encode metadata.");
return Status(Status::DRACO_ERROR, "Failed to encode metadata.");
}
return OkStatus();
}

View File

@ -85,7 +85,7 @@ class PointCloudEncoder {
virtual bool EncodeEncoderData() { return true; }
// Encodes any global geometry data (such as the number of points).
virtual bool EncodeGeometryData() { return true; }
virtual Status EncodeGeometryData() { return OkStatus(); }
// encode all attribute values. The attribute encoders are sorted to resolve
// any attribute dependencies and all the encoded data is stored into the

View File

@ -17,10 +17,10 @@
namespace draco {
bool PointCloudKdTreeEncoder::EncodeGeometryData() {
Status PointCloudKdTreeEncoder::EncodeGeometryData() {
const int32_t num_points = point_cloud()->num_points();
buffer()->Encode(num_points);
return true;
return OkStatus();
}
bool PointCloudKdTreeEncoder::GenerateAttributesEncoder(int32_t att_id) {

View File

@ -35,7 +35,7 @@ class PointCloudKdTreeEncoder : public PointCloudEncoder {
}
protected:
bool EncodeGeometryData() override;
Status EncodeGeometryData() override;
bool GenerateAttributesEncoder(int32_t att_id) override;
void ComputeNumberOfEncodedPoints() override;
};

View File

@ -19,10 +19,10 @@
namespace draco {
bool PointCloudSequentialEncoder::EncodeGeometryData() {
Status PointCloudSequentialEncoder::EncodeGeometryData() {
const int32_t num_points = point_cloud()->num_points();
buffer()->Encode(num_points);
return true;
return OkStatus();
}
bool PointCloudSequentialEncoder::GenerateAttributesEncoder(int32_t att_id) {

View File

@ -33,7 +33,7 @@ class PointCloudSequentialEncoder : public PointCloudEncoder {
}
protected:
bool EncodeGeometryData() override;
Status EncodeGeometryData() override;
bool GenerateAttributesEncoder(int32_t att_id) override;
void ComputeNumberOfEncodedPoints() override;
};

View File

@ -13,6 +13,7 @@
// limitations under the License.
//
#include "draco/core/data_buffer.h"
#include <algorithm>
namespace draco {

View File

@ -48,7 +48,7 @@ bool GenerateGoldenFile(const std::string &golden_file_name, const void *data,
bool CompareGoldenFile(const std::string &golden_file_name, const void *data,
int data_size) {
const std::string golden_path = GetTestFileFullPath(golden_file_name);
std::ifstream in_file(golden_path);
std::ifstream in_file(golden_path, std::ios::binary);
if (!in_file || data_size < 0)
return false;
const char *const data_c8 = static_cast<const char *>(data);

View File

@ -53,6 +53,11 @@ inline std::unique_ptr<Mesh> ReadMeshFromTestFile(const std::string &file_name,
const std::string path = GetTestFileFullPath(file_name);
return ReadMeshFromFile(path, use_metadata).value();
}
inline std::unique_ptr<Mesh> ReadMeshFromTestFile(const std::string &file_name,
const Options &options) {
const std::string path = GetTestFileFullPath(file_name);
return ReadMeshFromFile(path, options).value();
}
inline std::unique_ptr<PointCloud> ReadPointCloudFromTestFile(
const std::string &file_name) {

View File

@ -41,4 +41,21 @@ int32_t DataTypeLength(DataType dt) {
}
}
bool IsDataTypeIntegral(DataType dt) {
switch (dt) {
case DT_INT8:
case DT_UINT8:
case DT_INT16:
case DT_UINT16:
case DT_INT32:
case DT_UINT32:
case DT_INT64:
case DT_UINT64:
case DT_BOOL:
return true;
default:
return false;
}
}
} // namespace draco

View File

@ -41,6 +41,11 @@ enum DataType {
int32_t DataTypeLength(DataType dt);
// Equivalent to std::is_integral for draco::DataType. Returns true for all
// signed and unsigned integer types (including DT_BOOL). Returns false
// otherwise.
bool IsDataTypeIntegral(DataType dt);
} // namespace draco
#endif // DRACO_CORE_DRACO_TYPES_H_

View File

@ -18,7 +18,7 @@
namespace draco {
// Draco version is comprised of <major>.<minor>.<revision>.
static const char kDracoVersion[] = "1.3.4";
static const char kDracoVersion[] = "1.3.5";
const char *Version() { return kDracoVersion; }

View File

@ -32,12 +32,27 @@
#include <iostream>
namespace draco {
#ifndef DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName &) = delete; \
void operator=(const TypeName &) = delete;
#endif
#ifndef FALLTHROUGH_INTENDED
#define FALLTHROUGH_INTENDED void(0);
#if defined(__clang__) && defined(__has_warning)
#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
#define FALLTHROUGH_INTENDED [[clang::fallthrough]]
#endif
#elif defined(__GNUC__) && __GNUC__ >= 7
#define FALLTHROUGH_INTENDED [[gnu::fallthrough]]
#endif
// If FALLTHROUGH_INTENDED is still not defined, define it.
#ifndef FALLTHROUGH_INTENDED
#define FALLTHROUGH_INTENDED \
do { \
} while (0)
#endif
#endif
#ifndef LOG

View File

@ -17,6 +17,8 @@
#include <inttypes.h>
#include "draco/core/vector_d.h"
#define DRACO_INCREMENT_MOD(I, M) (((I) == ((M)-1)) ? 0 : ((I) + 1))
// Returns floor(sqrt(x)) where x is an integer number. The main intend of this

View File

@ -1,9 +1,12 @@
#include "draco/core/math_utils.h"
#include <cmath>
#include <random>
#include "draco/core/draco_test_base.h"
using draco::Vector3f;
TEST(MathUtils, Mod) { EXPECT_EQ(DRACO_INCREMENT_MOD(1, 1 << 1), 0); }
TEST(MathUtils, IntSqrt) {

View File

@ -16,11 +16,17 @@
#include <cstdlib>
#include <string>
#include <utility>
namespace draco {
Options::Options() {}
void Options::MergeAndReplace(const Options &other_options) {
for (const auto &item : other_options.options_)
options_[item.first] = item.second;
}
void Options::SetInt(const std::string &name, int val) {
options_[name] = std::to_string(val);
}

View File

@ -28,6 +28,11 @@ namespace draco {
class Options {
public:
Options();
// Merges |other_options| on top of the existing options of this instance
// replacing all entries that are present in both options instances.
void MergeAndReplace(const Options &other_options);
void SetInt(const std::string &name, int val);
void SetFloat(const std::string &name, float val);
void SetBool(const std::string &name, bool val);

View File

@ -25,7 +25,7 @@ class Status {
public:
enum Code {
OK = 0,
ERROR = -1, // Used for general errors.
DRACO_ERROR = -1, // Used for general errors.
IO_ERROR = -2, // Error when handling input or output stream.
INVALID_PARAMETER = -3, // Invalid parameter passed to a function.
UNSUPPORTED_VERSION = -4, // Input not compatible with the current version.

View File

@ -0,0 +1,81 @@
// Copyright 2017 The Draco Authors.
//
// 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.
//
#ifndef DRACO_CORE_STATUS_OR_H_
#define DRACO_CORE_STATUS_OR_H_
#include "draco/core/macros.h"
#include "draco/core/status.h"
namespace draco {
// Class StatusOr is used to wrap a Status along with a value of a specified
// type |T|. StatusOr is intended to be returned from functions in situations
// where it is desirable to carry over more information about the potential
// errors encountered during the function execution. If there are not errors,
// the caller can simply use the return value, otherwise the Status object
// provides more info about the encountered problem.
template <class T>
class StatusOr {
public:
StatusOr() {}
// Note: Constructors are intentionally not explicit to allow returning
// Status or the return value directly from functions.
StatusOr(const StatusOr &) = default;
StatusOr(StatusOr &&) = default;
StatusOr(const Status &status) : status_(status) {}
StatusOr(const T &value) : status_(OkStatus()), value_(value) {}
StatusOr(T &&value) : status_(OkStatus()), value_(std::move(value)) {}
StatusOr(const Status &status, const T &value)
: status_(status), value_(value) {}
const Status &status() const { return status_; }
const T &value() const & { return value_; }
const T &&value() const && { return std::move(value_); }
T &&value() && { return std::move(value_); }
// For consistency with existing Google StatusOr API we also include
// ValueOrDie() that currently returns the value().
const T &ValueOrDie() const & { return value(); }
T &&ValueOrDie() && { return std::move(value()); }
bool ok() const { return status_.ok(); }
private:
Status status_;
T value_;
};
// In case StatusOr<T> is ok(), this macro assigns value stored in StatusOr<T>
// to |lhs|, otherwise it returns the error Status.
//
// DRACO_ASSIGN_OR_RETURN(lhs, expression)
//
#define DRACO_ASSIGN_OR_RETURN(lhs, expression) \
DRACO_ASSIGN_OR_RETURN_IMPL_(DRACO_MACROS_IMPL_CONCAT_(_statusor, __LINE__), \
lhs, expression, _status)
// The actual implementation of the above macro.
#define DRACO_ASSIGN_OR_RETURN_IMPL_(statusor, lhs, expression, error_expr) \
auto statusor = (expression); \
if (!statusor.ok()) { \
auto _status = std::move(statusor.status()); \
(void)_status; /* error_expression may not use it */ \
return error_expr; \
} \
lhs = std::move(statusor).value();
} // namespace draco
#endif // DRACO_CORE_STATUS_OR_H_

View File

@ -27,8 +27,8 @@ class StatusTest : public ::testing::Test {
TEST_F(StatusTest, TestStatusOutput) {
// Tests that the Status can be stored in a provided std::ostream.
const draco::Status status(draco::Status::ERROR, "Error msg.");
ASSERT_EQ(status.code(), draco::Status::ERROR);
const draco::Status status(draco::Status::DRACO_ERROR, "Error msg.");
ASSERT_EQ(status.code(), draco::Status::DRACO_ERROR);
std::stringstream str;
str << status;

View File

@ -24,16 +24,20 @@
namespace draco {
// D-dimensional vector class with basic operations.
template <class CoeffT, int dimension_t>
template <class ScalarT, int dimension_t>
class VectorD {
public:
typedef VectorD<CoeffT, dimension_t> Self;
typedef CoeffT CoefficientType;
static constexpr int dimension = dimension_t;
typedef ScalarT Scalar;
typedef VectorD<Scalar, dimension_t> Self;
// TODO(hemmer): Deprecate.
typedef ScalarT CoefficientType;
VectorD() {
for (int i = 0; i < dimension_t; ++i)
(*this)[i] = CoeffT(0);
for (int i = 0; i < dimension; ++i)
(*this)[i] = Scalar(0);
}
// The following constructor does not compile in opt mode, which for now led
@ -42,58 +46,75 @@ class VectorD {
// template <typename... Args>
// explicit VectorD(Args... args) : v_({args...}) {}
VectorD(const CoeffT &c0, const CoeffT &c1) : v_({{c0, c1}}) {
DRACO_DCHECK_EQ(dimension_t, 2);
VectorD(const Scalar &c0, const Scalar &c1) : v_({{c0, c1}}) {
DRACO_DCHECK_EQ(dimension, 2);
v_[0] = c0;
v_[1] = c1;
}
VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2)
VectorD(const Scalar &c0, const Scalar &c1, const Scalar &c2)
: v_({{c0, c1, c2}}) {
DRACO_DCHECK_EQ(dimension_t, 3);
DRACO_DCHECK_EQ(dimension, 3);
}
VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2,
const CoeffT &c3)
VectorD(const Scalar &c0, const Scalar &c1, const Scalar &c2,
const Scalar &c3)
: v_({{c0, c1, c2, c3}}) {
DRACO_DCHECK_EQ(dimension_t, 4);
DRACO_DCHECK_EQ(dimension, 4);
}
VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2,
const CoeffT &c3, const CoeffT &c4)
VectorD(const Scalar &c0, const Scalar &c1, const Scalar &c2,
const Scalar &c3, const Scalar &c4)
: v_({{c0, c1, c2, c3, c4}}) {
DRACO_DCHECK_EQ(dimension_t, 5);
DRACO_DCHECK_EQ(dimension, 5);
}
VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2,
const CoeffT &c3, const CoeffT &c4, const CoeffT &c5)
VectorD(const Scalar &c0, const Scalar &c1, const Scalar &c2,
const Scalar &c3, const Scalar &c4, const Scalar &c5)
: v_({{c0, c1, c2, c3, c4, c5}}) {
DRACO_DCHECK_EQ(dimension_t, 6);
DRACO_DCHECK_EQ(dimension, 6);
}
VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2,
const CoeffT &c3, const CoeffT &c4, const CoeffT &c5,
const CoeffT &c6)
VectorD(const Scalar &c0, const Scalar &c1, const Scalar &c2,
const Scalar &c3, const Scalar &c4, const Scalar &c5,
const Scalar &c6)
: v_({{c0, c1, c2, c3, c4, c5, c6}}) {
DRACO_DCHECK_EQ(dimension_t, 7);
DRACO_DCHECK_EQ(dimension, 7);
}
VectorD(const Self &o) {
for (int i = 0; i < dimension_t; ++i)
for (int i = 0; i < dimension; ++i)
(*this)[i] = o[i];
}
CoeffT &operator[](int i) { return v_[i]; }
const CoeffT &operator[](int i) const { return v_[i]; }
// Constructs the vector from another vector with a different data type or a
// different number of components. If the |src_vector| has more components
// than |this| vector, the excess components are truncated. If the
// |src_vector| has fewer components than |this| vector, the remaining
// components are padded with 0.
// Note that the constructor is intentionally explicit to avoid accidental
// conversions between different vector types.
template <class OtherScalarT, int other_dimension_t>
explicit VectorD(const VectorD<OtherScalarT, other_dimension_t> &src_vector) {
for (int i = 0; i < dimension; ++i) {
if (i < other_dimension_t)
v_[i] = Scalar(src_vector[i]);
else
v_[i] = Scalar(0);
}
}
Scalar &operator[](int i) { return v_[i]; }
const Scalar &operator[](int i) const { return v_[i]; }
// TODO(hemmer): remove.
// Similar to interface of Eigen library.
CoeffT &operator()(int i) { return v_[i]; }
const CoeffT &operator()(int i) const { return v_[i]; }
Scalar &operator()(int i) { return v_[i]; }
const Scalar &operator()(int i) const { return v_[i]; }
// Unary operators.
Self operator-() const {
Self ret;
for (int i = 0; i < dimension_t; ++i) {
for (int i = 0; i < dimension; ++i) {
ret[i] = -(*this)[i];
}
return ret;
@ -102,7 +123,7 @@ class VectorD {
// Binary operators.
Self operator+(const Self &o) const {
Self ret;
for (int i = 0; i < dimension_t; ++i) {
for (int i = 0; i < dimension; ++i) {
ret[i] = (*this)[i] + o[i];
}
return ret;
@ -110,30 +131,46 @@ class VectorD {
Self operator-(const Self &o) const {
Self ret;
for (int i = 0; i < dimension_t; ++i) {
for (int i = 0; i < dimension; ++i) {
ret[i] = (*this)[i] - o[i];
}
return ret;
}
Self operator*(const CoeffT &o) const {
Self operator*(const Scalar &o) const {
Self ret;
for (int i = 0; i < dimension_t; ++i) {
for (int i = 0; i < dimension; ++i) {
ret[i] = (*this)[i] * o;
}
return ret;
}
Self operator/(const CoeffT &o) const {
Self operator/(const Scalar &o) const {
Self ret;
for (int i = 0; i < dimension_t; ++i) {
for (int i = 0; i < dimension; ++i) {
ret[i] = (*this)[i] / o;
}
return ret;
}
Self operator+(const Scalar &o) const {
Self ret;
for (int i = 0; i < dimension; ++i) {
ret[i] = (*this)[i] + o;
}
return ret;
}
Self operator-(const Scalar &o) const {
Self ret;
for (int i = 0; i < dimension; ++i) {
ret[i] = (*this)[i] - o;
}
return ret;
}
bool operator==(const Self &o) const {
for (int i = 0; i < dimension_t; ++i) {
for (int i = 0; i < dimension; ++i) {
if ((*this)[i] != o[i])
return false;
}
@ -143,67 +180,75 @@ class VectorD {
bool operator!=(const Self &x) const { return !((*this) == x); }
bool operator<(const Self &x) const {
for (int i = 0; i < dimension_t - 1; ++i) {
for (int i = 0; i < dimension - 1; ++i) {
if (v_[i] < x.v_[i])
return true;
if (v_[i] > x.v_[i])
return false;
}
// Only one check needed for the last dimension.
if (v_[dimension_t - 1] < x.v_[dimension_t - 1])
if (v_[dimension - 1] < x.v_[dimension - 1])
return true;
return false;
}
// Functions.
CoeffT SquaredNorm() const { return this->Dot(*this); }
Scalar SquaredNorm() const { return this->Dot(*this); }
// Computes L1, the sum of absolute values of all entries.
CoeffT AbsSum() const {
CoeffT result(0);
for (int i = 0; i < dimension_t; ++i) {
Scalar AbsSum() const {
Scalar result(0);
for (int i = 0; i < dimension; ++i) {
result += std::abs(v_[i]);
}
return result;
}
CoeffT Dot(const Self &o) const {
CoeffT ret(0);
for (int i = 0; i < dimension_t; ++i) {
Scalar Dot(const Self &o) const {
Scalar ret(0);
for (int i = 0; i < dimension; ++i) {
ret += (*this)[i] * o[i];
}
return ret;
}
void Normalize() {
const CoeffT magnitude = std::sqrt(this->SquaredNorm());
const Scalar magnitude = std::sqrt(this->SquaredNorm());
if (magnitude == 0) {
return;
}
for (int i = 0; i < dimension_t; ++i) {
for (int i = 0; i < dimension; ++i) {
(*this)[i] /= magnitude;
}
}
CoeffT *data() { return &(v_[0]); }
const Scalar &MaxCoeff() const {
return *std::max_element(v_.begin(), v_.end());
}
const Scalar &MinCoeff() const {
return *std::min_element(v_.begin(), v_.end());
}
Scalar *data() { return &(v_[0]); }
private:
std::array<CoeffT, dimension_t> v_;
std::array<Scalar, dimension> v_;
};
// Scalar multiplication from the other side too.
template <class CoeffT, int dimension_t>
VectorD<CoeffT, dimension_t> operator*(const CoeffT &o,
const VectorD<CoeffT, dimension_t> &v) {
template <class ScalarT, int dimension_t>
VectorD<ScalarT, dimension_t> operator*(
const ScalarT &o, const VectorD<ScalarT, dimension_t> &v) {
return v * o;
}
// Calculates the squared distance between two points.
template <class CoeffT, int dimension_t>
CoeffT SquaredDistance(const VectorD<CoeffT, dimension_t> &v1,
const VectorD<CoeffT, dimension_t> &v2) {
CoeffT difference;
CoeffT squared_distance = 0;
template <class ScalarT, int dimension_t>
ScalarT SquaredDistance(const VectorD<ScalarT, dimension_t> &v1,
const VectorD<ScalarT, dimension_t> &v2) {
ScalarT difference;
ScalarT squared_distance = 0;
// Check each index separately so difference is never negative and underflow
// is avoided for unsigned types.
for (int i = 0; i < dimension_t; ++i) {
@ -218,22 +263,22 @@ CoeffT SquaredDistance(const VectorD<CoeffT, dimension_t> &v1,
}
// Global function computing the cross product of two 3D vectors.
template <class CoeffT>
VectorD<CoeffT, 3> CrossProduct(const VectorD<CoeffT, 3> &u,
const VectorD<CoeffT, 3> &v) {
template <class ScalarT>
VectorD<ScalarT, 3> CrossProduct(const VectorD<ScalarT, 3> &u,
const VectorD<ScalarT, 3> &v) {
// Preventing accidental use with uint32_t and the like.
static_assert(std::is_signed<CoeffT>::value,
"CoeffT must be a signed type. ");
VectorD<CoeffT, 3> r;
static_assert(std::is_signed<ScalarT>::value,
"ScalarT must be a signed type. ");
VectorD<ScalarT, 3> r;
r[0] = (u[1] * v[2]) - (u[2] * v[1]);
r[1] = (u[2] * v[0]) - (u[0] * v[2]);
r[2] = (u[0] * v[1]) - (u[1] * v[0]);
return r;
}
template <class CoeffT, int dimension_t>
template <class ScalarT, int dimension_t>
inline std::ostream &operator<<(
std::ostream &out, const draco::VectorD<CoeffT, dimension_t> &vec) {
std::ostream &out, const draco::VectorD<ScalarT, dimension_t> &vec) {
for (int i = 0; i < dimension_t - 1; ++i) {
out << vec[i] << " ";
}

View File

@ -32,20 +32,17 @@ typedef draco::Vector5ui Vector5ui;
typedef draco::VectorD<int32_t, 3> Vector3i;
typedef draco::VectorD<int32_t, 4> Vector4i;
class VectorDTest : public ::testing::Test {
protected:
template <class CoeffT, int dimension_t>
void TestSquaredDistance(const draco::VectorD<CoeffT, dimension_t> v1,
const draco::VectorD<CoeffT, dimension_t> v2,
const CoeffT result) {
CoeffT squared_distance = SquaredDistance(v1, v2);
ASSERT_EQ(squared_distance, result);
squared_distance = SquaredDistance(v2, v1);
ASSERT_EQ(squared_distance, result);
}
};
template <class CoeffT, int dimension_t>
void TestSquaredDistance(const draco::VectorD<CoeffT, dimension_t> v1,
const draco::VectorD<CoeffT, dimension_t> v2,
const CoeffT result) {
CoeffT squared_distance = SquaredDistance(v1, v2);
ASSERT_EQ(squared_distance, result);
squared_distance = SquaredDistance(v2, v1);
ASSERT_EQ(squared_distance, result);
}
TEST_F(VectorDTest, TestOperators) {
TEST(VectorDTest, TestOperators) {
{
const Vector3f v;
ASSERT_EQ(v[0], 0);
@ -58,10 +55,8 @@ TEST_F(VectorDTest, TestOperators) {
ASSERT_EQ(v[2], 3);
Vector3f w = v;
bool comp = (v == w);
ASSERT_TRUE(comp);
comp = (v != w);
ASSERT_TRUE(!comp);
ASSERT_TRUE(v == w);
ASSERT_FALSE(v != w);
ASSERT_EQ(w[0], 1);
ASSERT_EQ(w[1], 2);
ASSERT_EQ(w[2], 3);
@ -81,10 +76,15 @@ TEST_F(VectorDTest, TestOperators) {
ASSERT_EQ(w[1], 2);
ASSERT_EQ(w[2], 3);
// Scalar multiplication from left and right.
w = v * 2.f;
ASSERT_EQ(w[0], 2);
ASSERT_EQ(w[1], 4);
ASSERT_EQ(w[2], 6);
w = 2.f * v;
ASSERT_EQ(w[0], 2);
ASSERT_EQ(w[1], 4);
ASSERT_EQ(w[2], 6);
ASSERT_EQ(v.SquaredNorm(), 14);
ASSERT_EQ(v.Dot(v), 14);
@ -109,7 +109,7 @@ TEST_F(VectorDTest, TestOperators) {
}
}
TEST_F(VectorDTest, TestSquaredDistance) {
TEST(VectorDTest, TestSquaredDistance) {
// Test Vector2f: float, 2D.
Vector2f v1_2f(5.5, 10.5);
Vector2f v2_2f(3.5, 15.5);
@ -158,7 +158,8 @@ TEST_F(VectorDTest, TestSquaredDistance) {
result_ui = 158;
TestSquaredDistance(v1_5ui, v2_5ui, result_ui);
}
TEST_F(VectorDTest, TestCrossProduct3D) {
TEST(VectorDTest, TestCrossProduct3D) {
const Vector3i e1(1, 0, 0);
const Vector3i e2(0, 1, 0);
const Vector3i e3(0, 0, 1);
@ -181,7 +182,7 @@ TEST_F(VectorDTest, TestCrossProduct3D) {
ASSERT_EQ(0, v2.Dot(orth));
}
TEST_F(VectorDTest, TestAbsSum) {
TEST(VectorDTest, TestAbsSum) {
// Testing const of function and zero.
const Vector3i v(0, 0, 0);
ASSERT_EQ(v.AbsSum(), 0);
@ -194,7 +195,18 @@ TEST_F(VectorDTest, TestAbsSum) {
ASSERT_EQ(Vector4i(-2, 4, -8, 3).AbsSum(), 17);
}
TEST_F(VectorDTest, TestOstream) {
TEST(VectorDTest, TestMinMaxCoeff) {
// Test verifies that MinCoeff() and MaxCoeff() functions work as intended.
const Vector4i vi(-10, 5, 2, 3);
ASSERT_EQ(vi.MinCoeff(), -10);
ASSERT_EQ(vi.MaxCoeff(), 5);
const Vector3f vf(6.f, 1000.f, -101.f);
ASSERT_EQ(vf.MinCoeff(), -101.f);
ASSERT_EQ(vf.MaxCoeff(), 1000.f);
}
TEST(VectorDTest, TestOstream) {
// Tests that the vector can be stored in a provided std::ostream.
const draco::VectorD<int64_t, 3> vector(1, 2, 3);
std::stringstream str;
@ -202,4 +214,22 @@ TEST_F(VectorDTest, TestOstream) {
ASSERT_EQ(str.str(), "1 2 3 ");
}
TEST(VectorDTest, TestConvertConstructor) {
// Tests that a vector can be constructed from another vector with a different
// type.
const draco::VectorD<int64_t, 3> vector(1, 2, 3);
const draco::VectorD<float, 3> vector3f(vector);
ASSERT_EQ(vector3f, draco::Vector3f(1.f, 2.f, 3.f));
const draco::VectorD<float, 2> vector2f(vector);
ASSERT_EQ(vector2f, draco::Vector2f(1.f, 2.f));
const draco::VectorD<float, 4> vector4f(vector3f);
ASSERT_EQ(vector4f, draco::Vector4f(1.f, 2.f, 3.f, 0.f));
const draco::VectorD<double, 1> vector1d(vector3f);
ASSERT_EQ(vector1d[0], 1.0);
}
} // namespace

View File

@ -16,37 +16,33 @@
#include <fstream>
#include "draco/io/file_utils.h"
#include "draco/io/obj_decoder.h"
#include "draco/io/parser_utils.h"
#include "draco/io/ply_decoder.h"
namespace draco {
namespace {
// Returns the file extension in lowercase if present, else ""
inline std::string LowercaseFileExtension(const std::string &filename) {
size_t pos = filename.find_last_of('.');
if (pos == std::string::npos || pos >= filename.length() - 1)
return "";
return parser::ToLower(filename.substr(pos + 1));
}
} // namespace
StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name) {
return ReadMeshFromFile(file_name, false);
const Options options;
return ReadMeshFromFile(file_name, options);
}
StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name,
bool use_metadata) {
Options options;
options.SetBool("use_metadata", use_metadata);
return ReadMeshFromFile(file_name, options);
}
StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name,
const Options &options) {
std::unique_ptr<Mesh> mesh(new Mesh());
// Analyze file extension.
const std::string extension = LowercaseFileExtension(file_name);
if (extension == "obj") {
// Wavefront OBJ file format.
ObjDecoder obj_decoder;
obj_decoder.set_use_metadata(use_metadata);
obj_decoder.set_use_metadata(options.GetBool("use_metadata", false));
const Status obj_status = obj_decoder.DecodeFromFile(file_name, mesh.get());
if (!obj_status.ok())
return obj_status;
@ -55,8 +51,7 @@ StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name,
if (extension == "ply") {
// Wavefront PLY file format.
PlyDecoder ply_decoder;
if (!ply_decoder.DecodeFromFile(file_name, mesh.get()))
return Status(Status::ERROR, "Unknown error.");
DRACO_RETURN_IF_ERROR(ply_decoder.DecodeFromFile(file_name, mesh.get()));
return std::move(mesh);
}
@ -64,9 +59,9 @@ StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name,
// draco encoding methods.
std::ifstream is(file_name.c_str(), std::ios::binary);
if (!is)
return Status(Status::ERROR, "Invalid input stream.");
return Status(Status::DRACO_ERROR, "Invalid input stream.");
if (!ReadMeshFromStream(&mesh, is).good())
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"Unknown error."); // Error reading the stream.
return std::move(mesh);
}

View File

@ -18,6 +18,7 @@
#include "draco/compression/config/compression_shared.h"
#include "draco/compression/decode.h"
#include "draco/compression/expert_encode.h"
#include "draco/core/options.h"
namespace draco {
@ -89,6 +90,13 @@ StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name);
StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name,
bool use_metadata);
// Reads a mesh from a file. Reading is configured with |options|:
// use_metadata : Read obj file info like material names and object names into
// metadata. Default is false.
// Returns nullptr with an error status if the decoding failed.
StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name,
const Options &options);
} // namespace draco
#endif // DRACO_MESH_MESH_IO_H_

View File

@ -18,6 +18,7 @@
#include <cmath>
#include <fstream>
#include "draco/io/file_utils.h"
#include "draco/io/parser_utils.h"
#include "draco/metadata/geometry_metadata.h"
@ -103,12 +104,12 @@ Status ObjDecoder::DecodeInternal() {
// Ensure the number of all entries is same for all attributes.
if (num_positions_ == 0)
return Status(Status::ERROR, "No position attribute");
return Status(Status::DRACO_ERROR, "No position attribute");
if (num_tex_coords_ > 0 && num_tex_coords_ != num_positions_)
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"Invalid number of texture coordinates for a point cloud");
if (num_normals_ > 0 && num_normals_ != num_positions_)
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"Invalid number of normals for a point cloud");
out_mesh_ = nullptr; // Treat the output geometry as a point cloud.
@ -151,12 +152,13 @@ Status ObjDecoder::DecodeInternal() {
}
if (num_materials_ > 0 && num_obj_faces_ > 0) {
GeometryAttribute va;
const auto geometry_attribute_type = GeometryAttribute::GENERIC;
if (num_materials_ < 256) {
va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT8, false, 1, 0);
va.Init(geometry_attribute_type, nullptr, 1, DT_UINT8, false, 1, 0);
} else if (num_materials_ < (1 << 16)) {
va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT16, false, 2, 0);
va.Init(geometry_attribute_type, nullptr, 1, DT_UINT16, false, 2, 0);
} else {
va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT32, false, 4, 0);
va.Init(geometry_attribute_type, nullptr, 1, DT_UINT32, false, 4, 0);
}
material_att_id_ =
out_point_cloud_->AddAttribute(va, false, num_materials_);
@ -234,10 +236,13 @@ Status ObjDecoder::DecodeInternal() {
out_mesh_->SetFace(i, face);
}
}
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
if (deduplicate_input_values_) {
out_point_cloud_->DeduplicateAttributeValues();
}
#endif
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
out_point_cloud_->DeduplicatePointIds();
#endif
return status;
@ -298,7 +303,7 @@ bool ObjDecoder::ParseVertexPosition(Status *status) {
for (int i = 0; i < 3; ++i) {
parser::SkipWhitespace(buffer());
if (!parser::ParseFloat(buffer(), val + i)) {
*status = Status(Status::ERROR, "Failed to parse a float number");
*status = Status(Status::DRACO_ERROR, "Failed to parse a float number");
// The definition is processed so return true.
return true;
}
@ -326,7 +331,7 @@ bool ObjDecoder::ParseNormal(Status *status) {
for (int i = 0; i < 3; ++i) {
parser::SkipWhitespace(buffer());
if (!parser::ParseFloat(buffer(), val + i)) {
*status = Status(Status::ERROR, "Failed to parse a float number");
*status = Status(Status::DRACO_ERROR, "Failed to parse a float number");
// The definition is processed so return true.
return true;
}
@ -354,7 +359,7 @@ bool ObjDecoder::ParseTexCoord(Status *status) {
for (int i = 0; i < 2; ++i) {
parser::SkipWhitespace(buffer());
if (!parser::ParseFloat(buffer(), val + i)) {
*status = Status(Status::ERROR, "Failed to parse a float number");
*status = Status(Status::DRACO_ERROR, "Failed to parse a float number");
// The definition is processed so return true.
return true;
}
@ -385,7 +390,7 @@ bool ObjDecoder::ParseFace(Status *status) {
if (i == 3) {
break; // It's OK if there is no fourth vertex index.
}
*status = Status(Status::ERROR, "Failed to parse vertex indices");
*status = Status(Status::DRACO_ERROR, "Failed to parse vertex indices");
return true;
}
++num_valid_indices;
@ -430,7 +435,8 @@ bool ObjDecoder::ParseFace(Status *status) {
}
}
if (num_indices < 3 || num_indices > 4) {
*status = Status(Status::ERROR, "Invalid number of indices on a face");
*status =
Status(Status::DRACO_ERROR, "Invalid number of indices on a face");
return false;
}
// Either one or two new triangles.
@ -455,7 +461,7 @@ bool ObjDecoder::ParseMaterialLib(Status *status) {
parser::SkipWhitespace(&line_buffer);
material_file_name_.clear();
if (!parser::ParseString(&line_buffer, &material_file_name_)) {
*status = Status(Status::ERROR, "Failed to parse material file name");
*status = Status(Status::DRACO_ERROR, "Failed to parse material file name");
return true;
}
parser::SkipLine(&line_buffer);
@ -492,6 +498,7 @@ bool ObjDecoder::ParseMaterial(Status * /* status */) {
// will be added to the list.
last_material_id_ = num_materials_;
material_name_to_id_[mat_name] = num_materials_++;
return true;
}
last_material_id_ = it->second;
@ -626,15 +633,7 @@ void ObjDecoder::MapPointToVertexIndices(
bool ObjDecoder::ParseMaterialFile(const std::string &file_name,
Status *status) {
// Get the correct path to the |file_name| using the folder from
// |input_file_name_| as the root folder.
const auto pos = input_file_name_.find_last_of("/\\");
std::string full_path;
if (pos != std::string::npos) {
full_path = input_file_name_.substr(0, pos + 1);
}
full_path += file_name;
const std::string full_path = GetFullPath(file_name, input_file_name_);
std::ifstream file(full_path, std::ios::binary);
if (!file)
return false;
@ -676,15 +675,14 @@ bool ObjDecoder::ParseMaterialFileDefinition(Status * /* status */) {
std::string str;
if (!parser::ParseString(buffer(), &str))
return false;
if (str.compare("newmtl") == 0) {
if (str == "newmtl") {
parser::SkipWhitespace(buffer());
parser::ParseLine(buffer(), &str);
if (str.length() == 0)
if (str.empty())
return false;
// Add new material to our map.
material_name_to_id_[str] = num_materials_++;
}
parser::SkipLine(buffer());
return true;
}

View File

@ -15,6 +15,8 @@
#ifndef DRACO_IO_OBJ_ENCODER_H_
#define DRACO_IO_OBJ_ENCODER_H_
#include <unordered_map>
#include "draco/core/encoder_buffer.h"
#include "draco/mesh/mesh.h"

View File

@ -128,7 +128,7 @@ bool ParseFloat(DecoderBuffer *buffer, float *value) {
return false;
// Apply exponent scaling to value.
v *= pow(10.0, exponent);
v *= pow(static_cast<double>(10.0), exponent);
}
}

View File

@ -17,28 +17,30 @@
#include <fstream>
#include "draco/core/macros.h"
#include "draco/core/status.h"
#include "draco/io/ply_property_reader.h"
namespace draco {
PlyDecoder::PlyDecoder() : out_mesh_(nullptr), out_point_cloud_(nullptr) {}
bool PlyDecoder::DecodeFromFile(const std::string &file_name, Mesh *out_mesh) {
Status PlyDecoder::DecodeFromFile(const std::string &file_name,
Mesh *out_mesh) {
out_mesh_ = out_mesh;
return DecodeFromFile(file_name, static_cast<PointCloud *>(out_mesh));
}
bool PlyDecoder::DecodeFromFile(const std::string &file_name,
PointCloud *out_point_cloud) {
Status PlyDecoder::DecodeFromFile(const std::string &file_name,
PointCloud *out_point_cloud) {
std::ifstream file(file_name, std::ios::binary);
if (!file)
return false;
return Status(Status::IO_ERROR, "Couldn't open file");
// Read the whole file into a buffer.
auto pos0 = file.tellg();
file.seekg(0, std::ios::end);
auto file_size = file.tellg() - pos0;
if (file_size == 0)
return false;
return Status(Status::IO_ERROR, "Zero file size");
file.seekg(0, std::ios::beg);
std::vector<char> data(file_size);
file.read(&data[0], file_size);
@ -47,44 +49,46 @@ bool PlyDecoder::DecodeFromFile(const std::string &file_name,
return DecodeFromBuffer(&buffer_, out_point_cloud);
}
bool PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh) {
Status PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh) {
out_mesh_ = out_mesh;
return DecodeFromBuffer(buffer, static_cast<PointCloud *>(out_mesh));
}
bool PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer,
PointCloud *out_point_cloud) {
Status PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer,
PointCloud *out_point_cloud) {
out_point_cloud_ = out_point_cloud;
buffer_.Init(buffer->data_head(), buffer->remaining_size());
return DecodeInternal();
}
bool PlyDecoder::DecodeInternal() {
Status PlyDecoder::DecodeInternal() {
PlyReader ply_reader;
if (!ply_reader.Read(buffer()))
return false;
DRACO_RETURN_IF_ERROR(ply_reader.Read(buffer()));
// First, decode the connectivity data.
if (out_mesh_ && !DecodeFaceData(ply_reader.GetElementByName("face")))
return false;
if (out_mesh_)
DRACO_RETURN_IF_ERROR(DecodeFaceData(ply_reader.GetElementByName("face")));
// Decode all attributes.
if (!DecodeVertexData(ply_reader.GetElementByName("vertex")))
return false;
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
DRACO_RETURN_IF_ERROR(
DecodeVertexData(ply_reader.GetElementByName("vertex")));
// In case there are no faces this is just a point cloud which does
// not require deduplication.
if (out_mesh_ && out_mesh_->num_faces() != 0) {
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
if (!out_point_cloud_->DeduplicateAttributeValues())
return false;
out_point_cloud_->DeduplicatePointIds();
}
return Status(Status::DRACO_ERROR,
"Could not deduplicate attribute values");
#endif
return true;
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
out_point_cloud_->DeduplicatePointIds();
#endif
}
return OkStatus();
}
bool PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
Status PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
// We accept point clouds now.
if (face_element == nullptr) {
return true;
return Status(Status::INVALID_PARAMETER, "face_element is null");
}
const int64_t num_faces = face_element->num_entries();
out_mesh_->SetNumFaces(num_faces);
@ -95,7 +99,7 @@ bool PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
vertex_indices = face_element->GetPropertyByName("vertex_index");
}
if (vertex_indices == nullptr || !vertex_indices->is_list()) {
return false; // No faces defined.
return Status(Status::DRACO_ERROR, "No faces defined");
}
PlyPropertyReader<PointIndex::ValueType> vertex_index_reader(vertex_indices);
@ -114,7 +118,7 @@ bool PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
face_index++;
}
out_mesh_->SetNumFaces(face_index.value());
return true;
return OkStatus();
}
template <typename DataTypeT>
@ -138,9 +142,9 @@ bool PlyDecoder::ReadPropertiesToAttribute(
return true;
}
bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
Status PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
if (vertex_element == nullptr)
return false;
return Status(Status::INVALID_PARAMETER, "vertex_element is null");
// TODO(ostava): For now, try to load x,y,z vertices and red,green,blue,alpha
// colors. We need to add other properties later.
const PlyProperty *const x_prop = vertex_element->GetPropertyByName("x");
@ -149,7 +153,7 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
if (!x_prop || !y_prop || !z_prop) {
// Currently, we require 3 vertex coordinates (this should be generalized
// later on).
return false;
return Status(Status::INVALID_PARAMETER, "x, y, or z property is missing");
}
const PointIndex::ValueType num_vertices = vertex_element->num_entries();
out_point_cloud_->set_num_points(num_vertices);
@ -158,12 +162,14 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
// All properties must have the same type.
if (x_prop->data_type() != y_prop->data_type() ||
y_prop->data_type() != z_prop->data_type()) {
return false;
return Status(Status::INVALID_PARAMETER,
"x, y, and z properties must have the same type");
}
// TODO(ostava): For now assume the position types are float32 or int32.
const DataType dt = x_prop->data_type();
if (dt != DT_FLOAT32 && dt != DT_INT32)
return false;
return Status(Status::INVALID_PARAMETER,
"x, y, and z properties must be of type float32 or int32");
GeometryAttribute va;
va.Init(GeometryAttribute::POSITION, nullptr, 3, dt, false,
@ -232,7 +238,8 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
// TODO(ostava): For now ensure the data type of all components is uint8.
DRACO_DCHECK_EQ(true, p->data_type() == DT_UINT8);
if (p->data_type() != DT_UINT8)
return false;
return Status(Status::INVALID_PARAMETER,
"Type of 'red' property must be uint8");
color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
new PlyPropertyReader<uint8_t>(p)));
}
@ -241,7 +248,8 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
// TODO(ostava): For now ensure the data type of all components is uint8.
DRACO_DCHECK_EQ(true, p->data_type() == DT_UINT8);
if (p->data_type() != DT_UINT8)
return false;
return Status(Status::INVALID_PARAMETER,
"Type of 'green' property must be uint8");
color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
new PlyPropertyReader<uint8_t>(p)));
}
@ -250,7 +258,8 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
// TODO(ostava): For now ensure the data type of all components is uint8.
DRACO_DCHECK_EQ(true, p->data_type() == DT_UINT8);
if (p->data_type() != DT_UINT8)
return false;
return Status(Status::INVALID_PARAMETER,
"Type of 'blue' property must be uint8");
color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
new PlyPropertyReader<uint8_t>(p)));
}
@ -259,7 +268,8 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
// TODO(ostava): For now ensure the data type of all components is uint8.
DRACO_DCHECK_EQ(true, p->data_type() == DT_UINT8);
if (p->data_type() != DT_UINT8)
return false;
return Status(Status::INVALID_PARAMETER,
"Type of 'alpha' property must be uint8");
color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
new PlyPropertyReader<uint8_t>(p)));
}
@ -279,7 +289,7 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
}
}
return true;
return OkStatus();
}
} // namespace draco

View File

@ -20,6 +20,7 @@
#include "draco/draco_features.h"
#include "draco/core/decoder_buffer.h"
#include "draco/core/status.h"
#include "draco/io/ply_reader.h"
#include "draco/mesh/mesh.h"
@ -35,21 +36,20 @@ class PlyDecoder {
PlyDecoder();
// Decodes an obj file stored in the input file.
// Returns nullptr if the decoding failed.
bool DecodeFromFile(const std::string &file_name, Mesh *out_mesh);
bool DecodeFromFile(const std::string &file_name,
PointCloud *out_point_cloud);
Status DecodeFromFile(const std::string &file_name, Mesh *out_mesh);
Status DecodeFromFile(const std::string &file_name,
PointCloud *out_point_cloud);
bool DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh);
bool DecodeFromBuffer(DecoderBuffer *buffer, PointCloud *out_point_cloud);
Status DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh);
Status DecodeFromBuffer(DecoderBuffer *buffer, PointCloud *out_point_cloud);
protected:
bool DecodeInternal();
Status DecodeInternal();
DecoderBuffer *buffer() { return &buffer_; }
private:
bool DecodeFaceData(const PlyElement *face_element);
bool DecodeVertexData(const PlyElement *vertex_element);
Status DecodeFaceData(const PlyElement *face_element);
Status DecodeVertexData(const PlyElement *vertex_element);
template <typename DataTypeT>
bool ReadPropertiesToAttribute(

View File

@ -26,8 +26,11 @@ class PlyDecoderTest : public ::testing::Test {
const std::string path = GetTestFileFullPath(file_name);
PlyDecoder decoder;
std::unique_ptr<Geometry> geometry(new Geometry());
if (!decoder.DecodeFromFile(path, geometry.get()))
Status status = decoder.DecodeFromFile(path, geometry.get());
if (!status.ok()) {
LOG(DRACO_ERROR) << "Failed to decode " << file_name << ": " << status;
return nullptr;
}
return geometry;
}

View File

@ -17,6 +17,7 @@
#include <array>
#include <regex>
#include "draco/core/status.h"
#include "draco/io/parser_utils.h"
#include "draco/io/ply_property_writer.h"
@ -34,13 +35,11 @@ PlyElement::PlyElement(const std::string &name, int64_t num_entries)
PlyReader::PlyReader() : format_(kLittleEndian) {}
bool PlyReader::Read(DecoderBuffer *buffer) {
error_message_.clear();
Status PlyReader::Read(DecoderBuffer *buffer) {
std::string value;
// The first line needs to by "ply".
if (!parser::ParseString(buffer, &value) || value != "ply") {
error_message_ = "Not a valid ply file.";
return false;
return Status(Status::INVALID_PARAMETER, "Not a valid ply file");
}
parser::SkipLine(buffer);
@ -52,52 +51,49 @@ bool PlyReader::Read(DecoderBuffer *buffer) {
format = words[1];
version = words[2];
} else {
error_message_ = "Missing or wrong format line.";
return false;
return Status(Status::INVALID_PARAMETER, "Missing or wrong format line");
}
if (version != "1.0") {
error_message_ = "Unsupported PLY version.";
return false; // Wrong version.
return Status(Status::UNSUPPORTED_VERSION, "Unsupported PLY version");
}
if (format == "binary_big_endian") {
error_message_ =
"Unsupported format. Currently we support only ascii and"
" binary_little_endian format.";
return false;
return Status(Status::UNSUPPORTED_VERSION,
"Unsupported format. Currently we support only ascii and"
" binary_little_endian format.");
}
if (format == "ascii") {
format_ = kAscii;
} else {
format_ = kLittleEndian;
}
if (!ParseHeader(buffer))
return false;
if (!ParsePropertiesData(buffer))
return false;
return true;
DRACO_RETURN_IF_ERROR(ParseHeader(buffer));
if (!ParsePropertiesData(buffer)) {
return Status(Status::INVALID_PARAMETER, "Couldn't parse properties");
}
return OkStatus();
}
bool PlyReader::ParseHeader(DecoderBuffer *buffer) {
while (error_message_.length() == 0 && !ParseEndHeader(buffer)) {
Status PlyReader::ParseHeader(DecoderBuffer *buffer) {
while (true) {
DRACO_ASSIGN_OR_RETURN(bool end, ParseEndHeader(buffer));
if (end)
break;
if (ParseElement(buffer))
continue;
if (ParseProperty(buffer))
DRACO_ASSIGN_OR_RETURN(bool property_parsed, ParseProperty(buffer));
if (property_parsed)
continue;
parser::SkipLine(buffer);
}
if (error_message_.length() > 0) {
printf("ERROR %s\n", error_message_.c_str());
return false;
}
return true;
return OkStatus();
}
bool PlyReader::ParseEndHeader(DecoderBuffer *buffer) {
StatusOr<bool> PlyReader::ParseEndHeader(DecoderBuffer *buffer) {
parser::SkipWhitespace(buffer);
std::array<char, 10> c;
if (!buffer->Peek(&c)) {
error_message_ = "End of file reached before the end_header.";
return false;
return Status(Status::INVALID_PARAMETER,
"End of file reached before the end_header");
}
if (std::memcmp(&c[0], "end_header", 10) != 0)
return false;
@ -126,7 +122,7 @@ bool PlyReader::ParseElement(DecoderBuffer *buffer) {
return true;
}
bool PlyReader::ParseProperty(DecoderBuffer *buffer) {
StatusOr<bool> PlyReader::ParseProperty(DecoderBuffer *buffer) {
if (elements_.empty())
return false; // Ignore properties if there is no active element.
DecoderBuffer line_buffer(*buffer);
@ -154,15 +150,13 @@ bool PlyReader::ParseProperty(DecoderBuffer *buffer) {
}
const DataType data_type = GetDataTypeFromString(data_type_str);
if (data_type == DT_INVALID) {
error_message_ = "Wrong property data type.";
return true; // Parsed.
return Status(Status::INVALID_PARAMETER, "Wrong property data type");
}
DataType list_type = DT_INVALID;
if (property_list_search) {
list_type = GetDataTypeFromString(list_type_str);
if (list_type == DT_INVALID) {
error_message_ = "Wrong property list type.";
return true; // Parsed.
return Status(Status::INVALID_PARAMETER, "Wrong property list type");
}
}
elements_.back().AddProperty(

View File

@ -26,6 +26,8 @@
#include "draco/core/decoder_buffer.h"
#include "draco/core/draco_types.h"
#include "draco/core/status.h"
#include "draco/core/status_or.h"
namespace draco {
@ -111,7 +113,7 @@ class PlyElement {
class PlyReader {
public:
PlyReader();
bool Read(DecoderBuffer *buffer);
Status Read(DecoderBuffer *buffer);
const PlyElement *GetElementByName(const std::string &name) const {
const auto it = element_index_.find(name);
@ -128,10 +130,10 @@ class PlyReader {
private:
enum Format { kLittleEndian = 0, kAscii };
bool ParseHeader(DecoderBuffer *buffer);
bool ParseEndHeader(DecoderBuffer *buffer);
Status ParseHeader(DecoderBuffer *buffer);
StatusOr<bool> ParseEndHeader(DecoderBuffer *buffer);
bool ParseElement(DecoderBuffer *buffer);
bool ParseProperty(DecoderBuffer *buffer);
StatusOr<bool> ParseProperty(DecoderBuffer *buffer);
bool ParsePropertiesData(DecoderBuffer *buffer);
bool ParseElementData(DecoderBuffer *buffer, int element_index);
bool ParseElementDataAscii(DecoderBuffer *buffer, int element_index);
@ -141,7 +143,6 @@ class PlyReader {
DataType GetDataTypeFromString(const std::string &name) const;
std::vector<PlyElement> elements_;
std::string error_message_;
std::map<std::string, int> element_index_;
Format format_;
};

View File

@ -45,7 +45,8 @@ TEST_F(PlyReaderTest, TestReader) {
DecoderBuffer buf;
buf.Init(data.data(), data.size());
PlyReader reader;
ASSERT_TRUE(reader.Read(&buf));
Status status = reader.Read(&buf);
ASSERT_TRUE(status.ok()) << status;
ASSERT_EQ(reader.num_elements(), 2);
ASSERT_EQ(reader.element(0).num_properties(), 7);
ASSERT_EQ(reader.element(1).num_properties(), 1);
@ -68,13 +69,15 @@ TEST_F(PlyReaderTest, TestReaderAscii) {
DecoderBuffer buf;
buf.Init(data.data(), data.size());
PlyReader reader;
ASSERT_TRUE(reader.Read(&buf));
Status status = reader.Read(&buf);
ASSERT_TRUE(status.ok()) << status;
const std::string file_name_ascii = "test_pos_color_ascii.ply";
const std::vector<char> data_ascii = ReadPlyFile(file_name_ascii);
buf.Init(data_ascii.data(), data_ascii.size());
PlyReader reader_ascii;
ASSERT_TRUE(reader_ascii.Read(&buf));
status = reader_ascii.Read(&buf);
ASSERT_TRUE(status.ok()) << status;
ASSERT_EQ(reader.num_elements(), reader_ascii.num_elements());
ASSERT_EQ(reader.element(0).num_properties(),
reader_ascii.element(0).num_properties());
@ -97,7 +100,8 @@ TEST_F(PlyReaderTest, TestReaderExtraWhitespace) {
DecoderBuffer buf;
buf.Init(data.data(), data.size());
PlyReader reader;
ASSERT_TRUE(reader.Read(&buf));
Status status = reader.Read(&buf);
ASSERT_TRUE(status.ok()) << status;
ASSERT_EQ(reader.num_elements(), 2);
ASSERT_EQ(reader.element(0).num_properties(), 7);
@ -121,7 +125,8 @@ TEST_F(PlyReaderTest, TestReaderMoreDataTypes) {
DecoderBuffer buf;
buf.Init(data.data(), data.size());
PlyReader reader;
ASSERT_TRUE(reader.Read(&buf));
Status status = reader.Read(&buf);
ASSERT_TRUE(status.ok()) << status;
ASSERT_EQ(reader.num_elements(), 2);
ASSERT_EQ(reader.element(0).num_properties(), 7);

View File

@ -40,8 +40,7 @@ StatusOr<std::unique_ptr<PointCloud>> ReadPointCloudFromFile(
if (extension == ".ply") {
// Wavefront PLY file format.
PlyDecoder ply_decoder;
if (!ply_decoder.DecodeFromFile(file_name, pc.get()))
return Status(Status::ERROR, "Unknown error.");
DRACO_RETURN_IF_ERROR(ply_decoder.DecodeFromFile(file_name, pc.get()));
return std::move(pc);
}
@ -49,9 +48,9 @@ StatusOr<std::unique_ptr<PointCloud>> ReadPointCloudFromFile(
// draco encoding methods.
std::ifstream is(file_name.c_str(), std::ios::binary);
if (!is)
return Status(Status::ERROR, "Invalid input stream.");
return Status(Status::DRACO_ERROR, "Invalid input stream.");
if (!ReadPointCloudFromStream(&pc, is).good())
return Status(Status::ERROR,
return Status(Status::DRACO_ERROR,
"Unknown error."); // Error reading the stream.
return std::move(pc);
}

View File

@ -16,6 +16,7 @@
#include <limits>
#include "draco/attributes/geometry_indices.h"
#include "draco/mesh/corner_table_iterators.h"
namespace draco {
@ -46,6 +47,8 @@ bool CornerTable::Init(const IndexTypeVector<FaceIndex, FaceType> &faces) {
int num_vertices = -1;
if (!ComputeOppositeCorners(&num_vertices))
return false;
if (!BreakNonManifoldEdges())
return false;
if (!ComputeVertexCorners(num_vertices))
return false;
return true;
@ -193,6 +196,110 @@ bool CornerTable::ComputeOppositeCorners(int *num_vertices) {
return true;
}
bool CornerTable::BreakNonManifoldEdges() {
// This function detects and breaks non-manifold edges that are caused by
// folds in 1-ring neighborhood around a vertex. Non-manifold edges can occur
// when the 1-ring surface around a vertex self-intersects in a common edge.
// For example imagine a surface around a pivot vertex 0, where the 1-ring
// is defined by vertices |1, 2, 3, 1, 4|. The surface passes edge <0, 1>
// twice which would result in a non-manifold edge that needs to be broken.
// For now all faces connected to these non-manifold edges are disconnected
// resulting in open boundaries on the mesh. New vertices will be created
// automatically for each new disjoint patch in the ComputeVertexCorners()
// method.
// Note that all other non-manifold edges are implicitly handled by the
// function ComputeVertexCorners() that automatically creates new vertices
// on disjoint 1-ring surface patches.
std::vector<bool> visited_corners(num_corners(), false);
std::vector<std::pair<VertexIndex, CornerIndex>> sink_vertices;
bool mesh_connectivity_updated = false;
do {
mesh_connectivity_updated = false;
for (CornerIndex c(0); c < num_corners(); ++c) {
if (visited_corners[c.value()])
continue;
sink_vertices.clear();
// First swing all the way to find the left-most corner connected to the
// corner's vertex.
CornerIndex first_c = c;
CornerIndex current_c = c;
CornerIndex next_c;
while (next_c = SwingLeft(current_c),
next_c != first_c && next_c != kInvalidCornerIndex &&
!visited_corners[next_c.value()]) {
current_c = next_c;
}
first_c = current_c;
// Swing right from the first corner and check if all visited edges
// are unique.
do {
visited_corners[current_c.value()] = true;
// Each new edge is defined by the pivot vertex (that is the same for
// all faces) and by the sink vertex (that is the |next| vertex from the
// currently processed pivot corner. I.e., each edge is uniquely defined
// by the sink vertex index.
const CornerIndex sink_c = Next(current_c);
const VertexIndex sink_v = corner_to_vertex_map_[sink_c];
// Corner that defines the edge on the face.
const CornerIndex edge_corner = Previous(current_c);
bool vertex_connectivity_updated = false;
// Go over all processed edges (sink vertices). If the current sink
// vertex has been already encountered before it may indicate a
// non-manifold edge that needs to be broken.
for (auto &&attached_sink_vertex : sink_vertices) {
if (attached_sink_vertex.first == sink_v) {
// Sink vertex has been already processed.
const CornerIndex other_edge_corner = attached_sink_vertex.second;
const CornerIndex opp_edge_corner = Opposite(edge_corner);
if (opp_edge_corner == other_edge_corner) {
// We are closing the loop so no need to change the connectivity.
continue;
}
// Break the connectivity on the non-manifold edge.
// TODO(ostava): It may be possible to reconnect the faces in a way
// that the final surface would be manifold.
const CornerIndex opp_other_edge_corner =
Opposite(other_edge_corner);
if (opp_edge_corner != kInvalidCornerIndex)
SetOppositeCorner(opp_edge_corner, kInvalidCornerIndex);
if (opp_other_edge_corner != kInvalidCornerIndex)
SetOppositeCorner(opp_other_edge_corner, kInvalidCornerIndex);
SetOppositeCorner(edge_corner, kInvalidCornerIndex);
SetOppositeCorner(other_edge_corner, kInvalidCornerIndex);
vertex_connectivity_updated = true;
break;
}
}
if (vertex_connectivity_updated) {
// Because of the updated connectivity, not all corners connected to
// this vertex have been processed and we need to go over them again.
// TODO(ostava): This can be optimized as we don't really need to
// iterate over all corners.
mesh_connectivity_updated = true;
break;
}
// Insert new sink vertex information <sink vertex index, edge corner>.
std::pair<VertexIndex, CornerIndex> new_sink_vert;
new_sink_vert.first = corner_to_vertex_map_[Previous(current_c)];
new_sink_vert.second = sink_c;
sink_vertices.push_back(new_sink_vert);
current_c = SwingRight(current_c);
} while (current_c != first_c && current_c != kInvalidCornerIndex);
}
} while (mesh_connectivity_updated);
return true;
}
bool CornerTable::ComputeVertexCorners(int num_vertices) {
DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
num_original_vertices_ = num_vertices;

View File

@ -51,7 +51,6 @@ namespace draco {
// non-manifold edges and vertices are automatically split.
class CornerTable {
public:
// TODO(hemmer): rename to Face.
// Corner table face type.
typedef std::array<VertexIndex, 3> FaceType;
@ -333,10 +332,14 @@ class CornerTable {
private:
// Computes opposite corners mapping from the data stored in
// |corner_to_vertex_map_|. Any non-manifold edge will be split so the result
// is always a 2-manifold surface.
// |corner_to_vertex_map_|.
bool ComputeOppositeCorners(int *num_vertices);
// Finds and breaks non-manifold edges in the 1-ring neighborhood around
// vertices (vertices themselves will be split in the ComputeVertexCorners()
// function if necessary).
bool BreakNonManifoldEdges();
// Computes the lookup map for going from a vertex to a corner. This method
// can handle non-manifold vertices by splitting them into multiple manifold
// vertices.

View File

@ -27,7 +27,7 @@ using conditional_t = typename std::conditional<B, T, F>::type;
Mesh::Mesh() {}
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
void Mesh::ApplyPointIdDeduplication(
const IndexTypeVector<PointIndex, PointIndex> &id_map,
const std::vector<PointIndex> &unique_point_ids) {

View File

@ -17,11 +17,11 @@
#include <memory>
#include "draco/draco_features.h"
#include "draco/attributes/geometry_indices.h"
#include "draco/core/hash_utils.h"
#include "draco/core/macros.h"
#include "draco/core/status.h"
#include "draco/draco_features.h"
#include "draco/point_cloud/point_cloud.h"
namespace draco {
@ -109,7 +109,7 @@ class Mesh : public PointCloud {
};
protected:
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
// Extends the point deduplication to face corners. This method is called from
// the PointCloud::DeduplicatePointIds() and it remaps all point ids stored in
// |faces_| to the new deduplicated point ids using the map |id_map|.

View File

@ -56,6 +56,25 @@ inline bool IsCornerOppositeToAttributeSeam(CornerIndex ci,
return false;
}
// Interpolates an attribute value on a face using given barycentric
// coordinates. InterpolatedVectorT should be a VectorD that corresponds to the
// values stored in the attribute.
// TODO(ostava): Find a better place for this.
template <typename InterpolatedVectorT>
InterpolatedVectorT ComputeInterpolatedAttributeValueOnMeshFace(
const Mesh &mesh, const PointAttribute &attribute, FaceIndex fi,
const std::array<float, 3> &barycentric_coord) {
const Mesh::Face &face = mesh.face(fi);
// Get values for all three corners of the face.
InterpolatedVectorT val[3];
for (int c = 0; c < 3; ++c) {
attribute.GetMappedValue(face[c], &(val[c][0]));
}
// Return an interpolated value.
return barycentric_coord[0] * val[0] + barycentric_coord[1] * val[1] +
barycentric_coord[2] * val[2];
}
} // namespace draco
#endif // DRACO_MESH_MESH_MISC_FUNCTIONS_H_

View File

@ -65,10 +65,12 @@ void TriangleSoupMeshBuilder::SetPerFaceAttributeValueForFace(
}
std::unique_ptr<Mesh> TriangleSoupMeshBuilder::Finalize() {
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
// First deduplicate attribute values.
if (!mesh_->DeduplicateAttributeValues())
return nullptr;
#endif
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
// Also deduplicate vertex indices.
mesh_->DeduplicatePointIds();
#endif

View File

@ -18,7 +18,7 @@
#include <cstring>
#include <memory>
#include <string>
#include <unordered_map>
#include <map>
#include <vector>
#include "draco/core/hash_utils.h"
@ -101,27 +101,45 @@ class Metadata {
// accessing entries of common data types. For now, developers need to know
// the type of entries they are requesting.
void AddEntryInt(const std::string &name, int32_t value);
// Returns false if Metadata does not contain an entry with a key of |name|.
// This function does not guarantee that entry's type is int32_t.
bool GetEntryInt(const std::string &name, int32_t *value) const;
void AddEntryIntArray(const std::string &name,
const std::vector<int32_t> &value);
// Returns false if Metadata does not contain an entry with a key of |name|.
// This function does not guarantee that entry's type is a vector of int32_t.
bool GetEntryIntArray(const std::string &name,
std::vector<int32_t> *value) const;
void AddEntryDouble(const std::string &name, double value);
// Returns false if Metadata does not contain an entry with a key of |name|.
// This function does not guarantee that entry's type is double.
bool GetEntryDouble(const std::string &name, double *value) const;
void AddEntryDoubleArray(const std::string &name,
const std::vector<double> &value);
// Returns false if Metadata does not contain an entry with a key of |name|.
// This function does not guarantee that entry's type is a vector of double.
bool GetEntryDoubleArray(const std::string &name,
std::vector<double> *value) const;
void AddEntryString(const std::string &name, const std::string &value);
// Returns false if Metadata does not contain an entry with a key of |name|.
// This function does not guarantee that entry's type is std::string.
bool GetEntryString(const std::string &name, std::string *value) const;
// Add a blob of data as an entry.
void AddEntryBinary(const std::string &name,
const std::vector<uint8_t> &value);
// Returns false if Metadata does not contain an entry with a key of |name|.
// This function does not guarantee that entry's type is a vector of uint8_t.
bool GetEntryBinary(const std::string &name,
std::vector<uint8_t> *value) const;
@ -132,10 +150,10 @@ class Metadata {
void RemoveEntry(const std::string &name);
int num_entries() const { return static_cast<int>(entries_.size()); }
const std::unordered_map<std::string, EntryValue> &entries() const {
const std::map<std::string, EntryValue> &entries() const {
return entries_;
}
const std::unordered_map<std::string, std::unique_ptr<Metadata>>
const std::map<std::string, std::unique_ptr<Metadata>>
&sub_metadatas() const {
return sub_metadatas_;
}
@ -160,8 +178,8 @@ class Metadata {
return itr->second.GetValue(entry_value);
}
std::unordered_map<std::string, EntryValue> entries_;
std::unordered_map<std::string, std::unique_ptr<Metadata>> sub_metadatas_;
std::map<std::string, EntryValue> entries_;
std::map<std::string, std::unique_ptr<Metadata>> sub_metadatas_;
friend struct MetadataHasher;
};

View File

@ -20,8 +20,7 @@ namespace draco {
bool MetadataEncoder::EncodeMetadata(EncoderBuffer *out_buffer,
const Metadata *metadata) {
const std::unordered_map<std::string, EntryValue> &entries =
metadata->entries();
const std::map<std::string, EntryValue> &entries = metadata->entries();
// Encode number of entries.
EncodeVarint(static_cast<uint32_t>(metadata->num_entries()), out_buffer);
// Encode all entries.
@ -33,8 +32,8 @@ bool MetadataEncoder::EncodeMetadata(EncoderBuffer *out_buffer,
EncodeVarint(data_size, out_buffer);
out_buffer->Encode(entry_value.data(), data_size);
}
const std::unordered_map<std::string, std::unique_ptr<Metadata>>
&sub_metadatas = metadata->sub_metadatas();
const std::map<std::string, std::unique_ptr<Metadata>> &sub_metadatas =
metadata->sub_metadatas();
// Encode number of sub-metadata
EncodeVarint(static_cast<uint32_t>(sub_metadatas.size()), out_buffer);
// Encode each sub-metadata

View File

@ -75,9 +75,9 @@ class MetadataEncoderTest : public ::testing::Test {
void CheckMetadatasAreEqual(const draco::Metadata &metadata0,
const draco::Metadata &metadata1) {
ASSERT_EQ(metadata0.num_entries(), metadata1.num_entries());
const std::unordered_map<std::string, draco::EntryValue> &entries0 =
const std::map<std::string, draco::EntryValue> &entries0 =
metadata0.entries();
const std::unordered_map<std::string, draco::EntryValue> &entries1 =
const std::map<std::string, draco::EntryValue> &entries1 =
metadata1.entries();
for (const auto &entry : entries0) {
const std::string &entry_name = entry.first;
@ -90,7 +90,7 @@ class MetadataEncoderTest : public ::testing::Test {
// Check nested metadata.
ASSERT_EQ(metadata0.sub_metadatas().size(),
metadata1.sub_metadatas().size());
const std::unordered_map<std::string, std::unique_ptr<draco::Metadata>>
const std::map<std::string, std::unique_ptr<draco::Metadata>>
&sub_metadatas0 = metadata0.sub_metadatas();
// Encode each sub-metadata
for (auto &&sub_metadata_entry0 : sub_metadatas0) {

View File

@ -87,23 +87,32 @@ int PointCloud::AddAttribute(std::unique_ptr<PointAttribute> pa) {
int PointCloud::AddAttribute(
const GeometryAttribute &att, bool identity_mapping,
AttributeValueIndex::ValueType num_attribute_values) {
const GeometryAttribute::Type type = att.attribute_type();
if (type == GeometryAttribute::INVALID)
auto pa = CreateAttribute(att, identity_mapping, num_attribute_values);
if (!pa)
return -1;
const int32_t att_id =
AddAttribute(std::unique_ptr<PointAttribute>(new PointAttribute(att)));
const int32_t att_id = AddAttribute(std::move(pa));
return att_id;
}
std::unique_ptr<PointAttribute> PointCloud::CreateAttribute(
const GeometryAttribute &att, bool identity_mapping,
AttributeValueIndex::ValueType num_attribute_values) const {
if (att.attribute_type() == GeometryAttribute::INVALID)
return nullptr;
std::unique_ptr<PointAttribute> pa =
std::unique_ptr<PointAttribute>(new PointAttribute(att));
// Initialize point cloud specific attribute data.
if (!identity_mapping) {
// First create mapping between indices.
attribute(att_id)->SetExplicitMapping(num_points_);
pa->SetExplicitMapping(num_points_);
} else {
attribute(att_id)->SetIdentityMapping();
attribute(att_id)->Resize(num_points_);
pa->SetIdentityMapping();
pa->Resize(num_points_);
}
if (num_attribute_values > 0) {
attribute(att_id)->Reset(num_attribute_values);
pa->Reset(num_attribute_values);
}
return att_id;
return pa;
}
void PointCloud::SetAttribute(int att_id, std::unique_ptr<PointAttribute> pa) {
@ -148,7 +157,7 @@ void PointCloud::DeleteAttribute(int att_id) {
}
}
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
void PointCloud::DeduplicatePointIds() {
// Hashing function for a single vertex.
auto point_hash = [this](PointIndex p) {
@ -214,7 +223,9 @@ void PointCloud::ApplyPointIdDeduplication(
attribute(a)->SetExplicitMapping(num_unique_points);
}
}
#endif
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
bool PointCloud::DeduplicateAttributeValues() {
// Go over all attributes and create mapping between duplicate entries.
if (num_points() == 0)

View File

@ -89,10 +89,17 @@ class PointCloud {
// PointAttribute::SetPointMapEntry() method. |num_attribute_values| can be
// used to specify the number of attribute values that are going to be
// stored in the newly created attribute. Returns attribute id of the newly
// created attribute.
// created attribute or -1 in case of failure.
int AddAttribute(const GeometryAttribute &att, bool identity_mapping,
AttributeValueIndex::ValueType num_attribute_values);
// Creates and returns a new attribute or nullptr in case of failure. This
// method is similar to AddAttribute(), except that it returns the new
// attribute instead of adding it to the point cloud.
std::unique_ptr<PointAttribute> CreateAttribute(
const GeometryAttribute &att, bool identity_mapping,
AttributeValueIndex::ValueType num_attribute_values) const;
// Assigns an attribute id to a given PointAttribute. If an attribute with
// the same attribute id already exists, it is deleted.
virtual void SetAttribute(int att_id, std::unique_ptr<PointAttribute> pa);
@ -101,11 +108,13 @@ class PointCloud {
// attribute ids of all subsequent attributes.
virtual void DeleteAttribute(int att_id);
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
// Deduplicates all attribute values (all attribute entries with the same
// value are merged into a single entry).
virtual bool DeduplicateAttributeValues();
#endif
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
// Removes duplicate point ids (two point ids are duplicate when all of their
// attributes are mapped to the same entry ids).
virtual void DeduplicatePointIds();
@ -173,7 +182,7 @@ class PointCloud {
void set_num_points(PointIndex::ValueType num) { num_points_ = num; }
protected:
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
// Applies id mapping of deduplicated points (called by DeduplicatePointIds).
virtual void ApplyPointIdDeduplication(
const IndexTypeVector<PointIndex, PointIndex> &id_map,

View File

@ -61,12 +61,14 @@ void PointCloudBuilder::SetAttributeValuesForAllPoints(
std::unique_ptr<PointCloud> PointCloudBuilder::Finalize(
bool deduplicate_points) {
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
if (deduplicate_points) {
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
point_cloud_->DeduplicateAttributeValues();
point_cloud_->DeduplicatePointIds();
}
#endif
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
point_cloud_->DeduplicatePointIds();
#endif
}
return std::move(point_cloud_);
}

View File

@ -134,7 +134,7 @@ int main(int argc, char **argv) {
}
// Save the decoded geometry into a file.
// TODO(ostava): Currently only .ply and .obj are supported.
// TODO(fgalligan): Change extension code to look for '.'.
const std::string extension = draco::parser::ToLower(
options.output.size() >= 4
? options.output.substr(options.output.size() - 4)
@ -167,7 +167,9 @@ int main(int argc, char **argv) {
}
}
} else {
printf("Invalid extension of the output file. Use either .ply or .obj\n");
printf(
"Invalid extension of the output file. Use either .ply, .obj, or "
".gltf\n");
return -1;
}
printf("Decoded geometry saved to %s (%" PRId64 " ms to decode)\n",

View File

@ -316,7 +316,7 @@ int main(int argc, char **argv) {
pc->GetNamedAttributeId(draco::GeometryAttribute::GENERIC, 0));
}
}
#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
#ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
// If any attribute has been deleted, run deduplication of point indices again
// as some points can be possibly combined.
if (options.tex_coords_deleted || options.normals_deleted ||

View File

@ -13,234 +13,147 @@
*/
/**
* 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>
* @date 2019-01-15
* @date 2019-11-29
*/
#include <iostream>
#include <fstream>
#include "draco-compressor.h"
#include <memory>
#include <sstream>
#include <vector>
#include "draco/mesh/mesh.h"
#include "draco/point_cloud/point_cloud.h"
#include "draco/core/vector_d.h"
#include "draco/io/mesh_io.h"
#include "draco/core/encoder_buffer.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
/**
* Prefix used for logging messages.
*/
const char *logTag = "DRACO-COMPRESSOR";
/**
* 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 {
/**
* All positions, normals and texture coordinates are appended to this mesh.
*/
draco::Mesh mesh;
/**
* One data buffer per attribute.
*/
// One data buffer per attribute.
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;
/**
* The id Draco assigns to the position attribute.
* 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.
*/
// Level of compression [0-10].
// Higher values mean slower encoding.
uint32_t compressionLevel = 7;
uint32_t quantizationBitsPosition = 14;
uint32_t quantizationBitsNormal = 10;
uint32_t quantizationBitsTexCoord = 12;
struct {
uint32_t positions = 14;
uint32_t normals = 10;
uint32_t uvs = 12;
uint32_t generic = 12;
} quantization;
};
draco::GeometryAttribute createAttribute(
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() {
DracoCompressor *create_compressor() {
return new DracoCompressor;
}
DLL_EXPORT(void) setCompressionLevel(
DracoCompressor *compressor,
uint32_t compressionLevel
void set_compression_level(
DracoCompressor *const compressor,
uint32_t const compressionLevel
) {
compressor->compressionLevel = compressionLevel;
}
DLL_EXPORT(void) setPositionQuantizationBits(
DracoCompressor *compressor,
uint32_t quantizationBitsPosition
void set_position_quantization(
DracoCompressor *const compressor,
uint32_t const quantizationBitsPosition
) {
compressor->quantizationBitsPosition = quantizationBitsPosition;
compressor->quantization.positions = quantizationBitsPosition;
}
DLL_EXPORT(void) setNormalQuantizationBits(
DracoCompressor *compressor,
uint32_t quantizationBitsNormal
void set_normal_quantization(
DracoCompressor *const compressor,
uint32_t const quantizationBitsNormal
) {
compressor->quantizationBitsNormal = quantizationBitsNormal;
compressor->quantization.normals = quantizationBitsNormal;
}
DLL_EXPORT(void) setTexCoordQuantizationBits(
DracoCompressor *compressor,
uint32_t quantizationBitsTexCoord
void set_uv_quantization(
DracoCompressor *const compressor,
uint32_t const quantizationBitsTexCoord
) {
compressor->quantizationBitsTexCoord = quantizationBitsTexCoord;
compressor->quantization.uvs = quantizationBitsTexCoord;
}
DLL_EXPORT(bool) compress(
DracoCompressor *compressor
void set_generic_quantization(
DracoCompressor *const compressor,
uint32_t const bits
) {
printf("%s: Compressing primitive:\n", logTag);
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);
compressor->quantization.generic = bits;
}
bool compress(
DracoCompressor *const compressor
) {
draco::Encoder encoder;
encoder.SetSpeedOptions(10 - compressor->compressionLevel, 10 - compressor->compressionLevel);
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, compressor->quantizationBitsPosition);
encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, compressor->quantizationBitsNormal);
encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, compressor->quantizationBitsTexCoord);
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);
draco::Status result = encoder.EncodeMeshToBuffer(compressor->mesh, &compressor->encoderBuffer);
if(!result.ok()) {
printf("%s: Could not compress mesh: %s\n", logTag, result.error_msg());
return false;
}
else {
return true;
}
return encoder.EncodeMeshToBuffer(compressor->mesh, &compressor->encoderBuffer).ok();
}
/**
* Returns the size of the compressed data in bytes.
*/
DLL_EXPORT(uint64_t) compressedSize(
DracoCompressor *compressor
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();
}
/**
* 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
void copy_to_bytes(
DracoCompressor const *const compressor,
uint8_t *const o_data
) {
memcpy(o_data, compressor->encoderBuffer.data(), compressedSize(compressor));
memcpy(o_data, compressor->encoderBuffer.data(), compressor->encoderBuffer.size());
}
DLL_EXPORT(uint32_t) getPositionAttributeId(
DracoCompressor *compressor
) {
return compressor->positionAttributeId;
}
DLL_EXPORT(uint32_t) getNormalAttributeId(
DracoCompressor *compressor
) {
return compressor->normalAttributeId;
}
DLL_EXPORT(uint32_t) getTexCoordAttributeIdCount(
DracoCompressor *compressor
) {
return (uint32_t) compressor->texCoordAttributeIds.size();
}
DLL_EXPORT(uint32_t) getTexCoordAttributeId(
DracoCompressor *compressor,
uint32_t index
) {
return compressor->texCoordAttributeIds[index];
}
/**
* Releases all memory allocated by the compressor.
*/
DLL_EXPORT(void) disposeCompressor(
DracoCompressor *compressor
void destroy_compressor(
DracoCompressor *const compressor
) {
delete compressor;
}
template<class T>
void setFaces(
draco::Mesh &mesh,
int numIndices,
T *indices
void set_faces_impl(
draco::Mesh &mesh,
int const index_count,
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 b = draco::PointIndex(indices[i + 1]);
@ -249,97 +162,116 @@ void setFaces(
}
}
DLL_EXPORT(void) setFaces(
DracoCompressor *compressor,
uint32_t numIndices,
uint32_t bytesPerIndex,
void *indices
void set_faces(
DracoCompressor *const compressor,
uint32_t const index_count,
uint32_t const index_byte_length,
uint8_t const *const indices
) {
switch (bytesPerIndex)
switch (index_byte_length)
{
case 1:
{
setFaces(compressor->mesh, numIndices, (uint8_t *) indices);
set_faces_impl(compressor->mesh, index_count, (uint8_t *) indices);
break;
}
case 2:
{
setFaces(compressor->mesh, numIndices, (uint16_t *) indices);
set_faces_impl(compressor->mesh, index_count, (uint16_t *) indices);
break;
}
case 4:
{
setFaces(compressor->mesh, numIndices, (uint32_t *) indices);
set_faces_impl(compressor->mesh, index_count, (uint32_t *) indices);
break;
}
default:
{
printf("%s: Unsupported index size %d\n", logTag, bytesPerIndex);
printf("%s: Unsupported index size %d\n", logTag, index_byte_length);
break;
}
}
}
void addFloatAttribute(
DracoCompressor *compressor,
draco::GeometryAttribute::Type type,
uint32_t count,
uint8_t componentCount,
float *source
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::DataBuffer>();
const auto attribute = createAttribute(type, *buffer, componentCount);
draco::GeometryAttribute attribute;
const auto id = (const uint32_t) compressor->mesh.AddAttribute(attribute, false, count);
compressor->mesh.attribute(id)->SetIdentityMapping();
attribute.Init(
semantics,
&*buffer,
component_count,
data_type,
false,
component_size * component_count,
0
);
switch (type)
{
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;
}
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),
source + i * componentCount
draco::AttributeValueIndex(i),
data + i * component_count * component_size
);
}
compressor->buffers.emplace_back(std::move(buffer));
return id;
}
DLL_EXPORT(void) addPositionAttribute(
DracoCompressor *compressor,
uint32_t count,
float *source
uint32_t add_positions_f32(
DracoCompressor *const compressor,
uint32_t const count,
uint8_t const *const data
) {
addFloatAttribute(compressor, draco::GeometryAttribute::POSITION, count, 3, source);
return add_attribute_to_mesh(compressor, draco::GeometryAttribute::POSITION,
draco::DT_FLOAT32, count, 3, sizeof(float), data);
}
DLL_EXPORT(void) addNormalAttribute(
DracoCompressor *compressor,
uint32_t count,
float *source
uint32_t add_normals_f32(
DracoCompressor *const compressor,
uint32_t const count,
uint8_t const *const data
) {
addFloatAttribute(compressor, draco::GeometryAttribute::NORMAL, count, 3, source);
return add_attribute_to_mesh(compressor, draco::GeometryAttribute::NORMAL,
draco::DT_FLOAT32, count, 3, sizeof(float), data);
}
DLL_EXPORT(void) addTexCoordAttribute(
DracoCompressor *compressor,
uint32_t count,
float *source
uint32_t add_uvs_f32(
DracoCompressor *const compressor,
uint32_t const count,
uint8_t const *const data
) {
addFloatAttribute(compressor, draco::GeometryAttribute::TEX_COORD, count, 2, source);
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);
}

173
extern/draco/src/draco-compressor.h vendored Normal file
View File

@ -0,0 +1,173 @@
/*
* 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 <eckerlein@ux3d.io>
* @date 2019-11-29
*/
#include <cstdint>
#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
);