// 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 using std::unordered_map; // Shortcut for typed conditionals. template using conditional_t = typename std::conditional::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(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(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( 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(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(num_attribute_values); return true; } void PointAttribute::Resize(size_t new_num_unique_entries) { num_unique_entries_ = static_cast(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(in_att, in_att_offset); break; case DT_INT8: unique_vals = DeduplicateTypedValues(in_att, in_att_offset); break; case DT_UINT8: case DT_BOOL: unique_vals = DeduplicateTypedValues(in_att, in_att_offset); break; case DT_UINT16: unique_vals = DeduplicateTypedValues(in_att, in_att_offset); break; case DT_INT16: unique_vals = DeduplicateTypedValues(in_att, in_att_offset); break; case DT_UINT32: unique_vals = DeduplicateTypedValues(in_att, in_att_offset); break; case DT_INT32: unique_vals = DeduplicateTypedValues(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 // with the correct template arguments. // Returns the number of unique attribute values. template 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(in_att, in_att_offset); case 2: return DeduplicateFormattedValues(in_att, in_att_offset); case 3: return DeduplicateFormattedValues(in_att, in_att_offset); case 4: return DeduplicateFormattedValues(in_att, in_att_offset); default: return 0; } } template 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 . If bool-expression // is true the "true" branch is used and vice versa. All at compile time. typedef conditional_t>> HashType; AttributeValueIndex unique_vals(0); typedef std::array AttributeValue; typedef std::array AttributeHashableValue; // Hash map storing index of the first attribute with a given value. unordered_map> value_to_index_map; AttributeValue att_value; AttributeHashableValue hashable_value; IndexTypeVector 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(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(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(indices_map_.size()); ++i) { SetPointMapEntry(i, value_map[indices_map_[i]]); } } num_unique_entries_ = unique_vals.value(); return num_unique_entries_; } #endif } // namespace draco