forked from bartvdbraak/blender
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:
parent
05ef758f46
commit
9febff7e14
2
extern/draco/CMakeLists.txt
vendored
2
extern/draco/CMakeLists.txt
vendored
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
@ -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()(
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
4
extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.cc
vendored
4
extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.cc
vendored
@ -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;
|
||||
}
|
||||
|
6
extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.cc
vendored
6
extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.cc
vendored
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
@ -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) \
|
||||
#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) \
|
||||
#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
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
2
extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h
vendored
2
extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h
vendored
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -87,7 +87,7 @@ TEST_P(MeshEncoderTest, EncodeGoldenMesh) {
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MeshEncoderTests, MeshEncoderTest,
|
||||
INSTANTIATE_TEST_SUITE_P(MeshEncoderTests, MeshEncoderTest,
|
||||
::testing::Values("sequential", "edgebreaker"));
|
||||
|
||||
} // namespace draco
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
8
extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.h
vendored
8
extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.h
vendored
@ -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,
|
||||
|
2
extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_compression_method.h
vendored
2
extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_compression_method.h
vendored
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
4
extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.cc
vendored
4
extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.cc
vendored
@ -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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
#include "draco/core/data_buffer.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace draco {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -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; }
|
||||
|
||||
|
17
extern/draco/dracoenc/src/draco/core/macros.h
vendored
17
extern/draco/dracoenc/src/draco/core/macros.h
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
81
extern/draco/dracoenc/src/draco/core/status_or.h
vendored
Normal file
81
extern/draco/dracoenc/src/draco/core/status_or.h
vendored
Normal 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_
|
@ -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;
|
||||
|
173
extern/draco/dracoenc/src/draco/core/vector_d.h
vendored
173
extern/draco/dracoenc/src/draco/core/vector_d.h
vendored
@ -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] << " ";
|
||||
}
|
||||
|
@ -32,8 +32,6 @@ 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,
|
||||
@ -43,9 +41,8 @@ class VectorDTest : public ::testing::Test {
|
||||
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
|
||||
|
33
extern/draco/dracoenc/src/draco/io/mesh_io.cc
vendored
33
extern/draco/dracoenc/src/draco/io/mesh_io.cc
vendored
@ -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);
|
||||
}
|
||||
|
8
extern/draco/dracoenc/src/draco/io/mesh_io.h
vendored
8
extern/draco/dracoenc/src/draco/io/mesh_io.h
vendored
@ -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_
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
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,
|
||||
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
|
||||
|
16
extern/draco/dracoenc/src/draco/io/ply_decoder.h
vendored
16
extern/draco/dracoenc/src/draco/io/ply_decoder.h
vendored
@ -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,
|
||||
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(
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
58
extern/draco/dracoenc/src/draco/io/ply_reader.cc
vendored
58
extern/draco/dracoenc/src/draco/io/ply_reader.cc
vendored
@ -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_ =
|
||||
return Status(Status::UNSUPPORTED_VERSION,
|
||||
"Unsupported format. Currently we support only ascii and"
|
||||
" binary_little_endian format.";
|
||||
return false;
|
||||
" 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(
|
||||
|
11
extern/draco/dracoenc/src/draco/io/ply_reader.h
vendored
11
extern/draco/dracoenc/src/draco/io/ply_reader.h
vendored
@ -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_;
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
107
extern/draco/dracoenc/src/draco/mesh/corner_table.cc
vendored
107
extern/draco/dracoenc/src/draco/mesh/corner_table.cc
vendored
@ -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;
|
||||
|
@ -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.
|
||||
|
2
extern/draco/dracoenc/src/draco/mesh/mesh.cc
vendored
2
extern/draco/dracoenc/src/draco/mesh/mesh.cc
vendored
@ -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) {
|
||||
|
6
extern/draco/dracoenc/src/draco/mesh/mesh.h
vendored
6
extern/draco/dracoenc/src/draco/mesh/mesh.h
vendored
@ -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|.
|
||||
|
@ -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_
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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_);
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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 ||
|
||||
|
368
extern/draco/src/draco-compressor.cpp
vendored
368
extern/draco/src/draco-compressor.cpp
vendored
@ -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(
|
||||
void set_faces_impl(
|
||||
draco::Mesh &mesh,
|
||||
int numIndices,
|
||||
T *indices
|
||||
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
|
||||
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
173
extern/draco/src/draco-compressor.h
vendored
Normal 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
|
||||
);
|
Loading…
Reference in New Issue
Block a user