assimp/contrib/draco/src/draco/attributes/point_attribute.cc

269 lines
10 KiB
C++

// Copyright 2016 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.
//
#include "draco/attributes/point_attribute.h"
#include <unordered_map>
using std::unordered_map;
// Shortcut for typed conditionals.
template <bool B, class T, class F>
using conditional_t = typename std::conditional<B, T, F>::type;
namespace draco {
PointAttribute::PointAttribute()
: num_unique_entries_(0), identity_mapping_(false) {}
PointAttribute::PointAttribute(const GeometryAttribute &att)
: GeometryAttribute(att),
num_unique_entries_(0),
identity_mapping_(false) {}
void PointAttribute::Init(Type attribute_type, int8_t num_components,
DataType data_type, bool normalized,
size_t num_attribute_values) {
attribute_buffer_ = std::unique_ptr<DataBuffer>(new DataBuffer());
GeometryAttribute::Init(attribute_type, attribute_buffer_.get(),
num_components, data_type, normalized,
DataTypeLength(data_type) * num_components, 0);
Reset(num_attribute_values);
SetIdentityMapping();
}
void PointAttribute::CopyFrom(const PointAttribute &src_att) {
if (buffer() == nullptr) {
// If the destination attribute doesn't have a valid buffer, create it.
attribute_buffer_ = std::unique_ptr<DataBuffer>(new DataBuffer());
ResetBuffer(attribute_buffer_.get(), 0, 0);
}
if (!GeometryAttribute::CopyFrom(src_att)) {
return;
}
identity_mapping_ = src_att.identity_mapping_;
num_unique_entries_ = src_att.num_unique_entries_;
indices_map_ = src_att.indices_map_;
if (src_att.attribute_transform_data_) {
attribute_transform_data_ = std::unique_ptr<AttributeTransformData>(
new AttributeTransformData(*src_att.attribute_transform_data_));
} else {
attribute_transform_data_ = nullptr;
}
}
bool PointAttribute::Reset(size_t num_attribute_values) {
if (attribute_buffer_ == nullptr) {
attribute_buffer_ = std::unique_ptr<DataBuffer>(new DataBuffer());
}
const int64_t entry_size = DataTypeLength(data_type()) * num_components();
if (!attribute_buffer_->Update(nullptr, num_attribute_values * entry_size)) {
return false;
}
// Assign the new buffer to the parent attribute.
ResetBuffer(attribute_buffer_.get(), entry_size, 0);
num_unique_entries_ = static_cast<uint32_t>(num_attribute_values);
return true;
}
void PointAttribute::Resize(size_t new_num_unique_entries) {
num_unique_entries_ = static_cast<uint32_t>(new_num_unique_entries);
attribute_buffer_->Resize(new_num_unique_entries * byte_stride());
}
#ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
AttributeValueIndex::ValueType PointAttribute::DeduplicateValues(
const GeometryAttribute &in_att) {
return DeduplicateValues(in_att, AttributeValueIndex(0));
}
AttributeValueIndex::ValueType PointAttribute::DeduplicateValues(
const GeometryAttribute &in_att, AttributeValueIndex in_att_offset) {
AttributeValueIndex::ValueType unique_vals = 0;
switch (in_att.data_type()) {
// Currently we support only float, uint8, and uint16 arguments.
case DT_FLOAT32:
unique_vals = DeduplicateTypedValues<float>(in_att, in_att_offset);
break;
case DT_INT8:
unique_vals = DeduplicateTypedValues<int8_t>(in_att, in_att_offset);
break;
case DT_UINT8:
case DT_BOOL:
unique_vals = DeduplicateTypedValues<uint8_t>(in_att, in_att_offset);
break;
case DT_UINT16:
unique_vals = DeduplicateTypedValues<uint16_t>(in_att, in_att_offset);
break;
case DT_INT16:
unique_vals = DeduplicateTypedValues<int16_t>(in_att, in_att_offset);
break;
case DT_UINT32:
unique_vals = DeduplicateTypedValues<uint32_t>(in_att, in_att_offset);
break;
case DT_INT32:
unique_vals = DeduplicateTypedValues<int32_t>(in_att, in_att_offset);
break;
default:
return -1; // Unsupported data type.
}
if (unique_vals == 0) {
return -1; // Unexpected error.
}
return unique_vals;
}
// Helper function for calling UnifyDuplicateAttributes<T,num_components_t>
// with the correct template arguments.
// Returns the number of unique attribute values.
template <typename T>
AttributeValueIndex::ValueType PointAttribute::DeduplicateTypedValues(
const GeometryAttribute &in_att, AttributeValueIndex in_att_offset) {
// Select the correct method to call based on the number of attribute
// components.
switch (in_att.num_components()) {
case 1:
return DeduplicateFormattedValues<T, 1>(in_att, in_att_offset);
case 2:
return DeduplicateFormattedValues<T, 2>(in_att, in_att_offset);
case 3:
return DeduplicateFormattedValues<T, 3>(in_att, in_att_offset);
case 4:
return DeduplicateFormattedValues<T, 4>(in_att, in_att_offset);
default:
return 0;
}
}
template <typename T, int num_components_t>
AttributeValueIndex::ValueType PointAttribute::DeduplicateFormattedValues(
const GeometryAttribute &in_att, AttributeValueIndex in_att_offset) {
// We want to detect duplicates using a hash map but we cannot hash floating
// point numbers directly so bit-copy floats to the same sized integers and
// hash them.
// First we need to determine which int type to use (1, 2, 4 or 8 bytes).
// Note, this is done at compile time using std::conditional struct.
// Conditional is in form <bool-expression, true, false>. If bool-expression
// is true the "true" branch is used and vice versa. All at compile time.
typedef conditional_t<sizeof(T) == 1, uint8_t,
conditional_t<sizeof(T) == 2, uint16_t,
conditional_t<sizeof(T) == 4, uint32_t,
/*else*/ uint64_t>>>
HashType;
AttributeValueIndex unique_vals(0);
typedef std::array<T, num_components_t> AttributeValue;
typedef std::array<HashType, num_components_t> AttributeHashableValue;
// Hash map storing index of the first attribute with a given value.
unordered_map<AttributeHashableValue, AttributeValueIndex,
HashArray<AttributeHashableValue>>
value_to_index_map;
AttributeValue att_value;
AttributeHashableValue hashable_value;
IndexTypeVector<AttributeValueIndex, AttributeValueIndex> value_map(
num_unique_entries_);
for (AttributeValueIndex i(0); i < num_unique_entries_; ++i) {
const AttributeValueIndex att_pos = i + in_att_offset;
att_value = in_att.GetValue<T, num_components_t>(att_pos);
// Convert the value to hashable type. Bit-copy real attributes to integers.
memcpy(&(hashable_value[0]), &(att_value[0]), sizeof(att_value));
// Check if the given attribute value has been used before already.
auto it = value_to_index_map.find(hashable_value);
if (it != value_to_index_map.end()) {
// Duplicated value found. Update index mapping.
value_map[i] = it->second;
} else {
// New unique value.
// Update the hash map with a new entry pointing to the latest unique
// vertex index.
value_to_index_map.insert(
std::pair<AttributeHashableValue, AttributeValueIndex>(hashable_value,
unique_vals));
// Add the unique value to the mesh builder.
SetAttributeValue(unique_vals, &att_value);
// Update index mapping.
value_map[i] = unique_vals;
++unique_vals;
}
}
if (unique_vals == num_unique_entries_) {
return unique_vals.value(); // Nothing has changed.
}
if (is_mapping_identity()) {
// Change identity mapping to the explicit one.
// The number of points is equal to the number of old unique values.
SetExplicitMapping(num_unique_entries_);
// Update the explicit map.
for (uint32_t i = 0; i < num_unique_entries_; ++i) {
SetPointMapEntry(PointIndex(i), value_map[AttributeValueIndex(i)]);
}
} else {
// Update point to value map using the mapping between old and new values.
for (PointIndex i(0); i < static_cast<uint32_t>(indices_map_.size()); ++i) {
SetPointMapEntry(i, value_map[indices_map_[i]]);
}
}
num_unique_entries_ = unique_vals.value();
return num_unique_entries_;
}
#endif
#ifdef DRACO_TRANSCODER_SUPPORTED
void PointAttribute::RemoveUnusedValues() {
if (is_mapping_identity()) {
return; // For identity mapping, all values are always used.
}
// For explicit mapping we need to check if any point is mapped to a value.
// If not we can delete the value.
IndexTypeVector<AttributeValueIndex, bool> is_value_used(size(), false);
int num_used_values = 0;
for (PointIndex pi(0); pi < indices_map_.size(); ++pi) {
const AttributeValueIndex avi = indices_map_[pi];
if (!is_value_used[avi]) {
is_value_used[avi] = true;
num_used_values++;
}
}
if (num_used_values == size()) {
return; // All values are used.
}
// Remap the values and update the point to value mapping.
IndexTypeVector<AttributeValueIndex, AttributeValueIndex>
old_to_new_value_map(size(), kInvalidAttributeValueIndex);
AttributeValueIndex new_avi(0);
for (AttributeValueIndex avi(0); avi < size(); ++avi) {
if (!is_value_used[avi]) {
continue;
}
if (avi != new_avi) {
SetAttributeValue(new_avi, GetAddress(avi));
}
old_to_new_value_map[avi] = new_avi++;
}
// Remap all points to the new attribute values.
for (PointIndex pi(0); pi < indices_map_.size(); ++pi) {
indices_map_[pi] = old_to_new_value_map[indices_map_[pi]];
}
num_unique_entries_ = num_used_values;
}
#endif
} // namespace draco