1 // Copyright 2016 The Draco Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 #ifndef DRACO_ATTRIBUTES_POINT_ATTRIBUTE_H_
16 #define DRACO_ATTRIBUTES_POINT_ATTRIBUTE_H_
17 
18 #include <memory>
19 
20 #include "draco/attributes/attribute_transform_data.h"
21 #include "draco/attributes/geometry_attribute.h"
22 #include "draco/core/draco_index_type_vector.h"
23 #include "draco/core/hash_utils.h"
24 #include "draco/core/macros.h"
25 #include "draco/draco_features.h"
26 
27 namespace draco {
28 
29 // Class for storing point specific data about each attribute. In general,
30 // multiple points stored in a point cloud can share the same attribute value
31 // and this class provides the necessary mapping between point ids and attribute
32 // value ids.
33 class PointAttribute : public GeometryAttribute {
34  public:
35   PointAttribute();
36   explicit PointAttribute(const GeometryAttribute &att);
37 
38   // Make sure the move constructor is defined (needed for better performance
39   // when new attributes are added to PointCloud).
40   PointAttribute(PointAttribute &&attribute) = default;
41   PointAttribute &operator=(PointAttribute &&attribute) = default;
42 
43   // Initializes a point attribute. By default the attribute will be set to
44   // identity mapping between point indices and attribute values. To set custom
45   // mapping use SetExplicitMapping() function.
46   void Init(Type attribute_type, int8_t num_components, DataType data_type,
47             bool normalized, size_t num_attribute_values);
48 
49   // Copies attribute data from the provided |src_att| attribute.
50   void CopyFrom(const PointAttribute &src_att);
51 
52   // Prepares the attribute storage for the specified number of entries.
53   bool Reset(size_t num_attribute_values);
54 
size()55   size_t size() const { return num_unique_entries_; }
mapped_index(PointIndex point_index)56   AttributeValueIndex mapped_index(PointIndex point_index) const {
57     if (identity_mapping_) {
58       return AttributeValueIndex(point_index.value());
59     }
60     return indices_map_[point_index];
61   }
buffer()62   DataBuffer *buffer() const { return attribute_buffer_.get(); }
is_mapping_identity()63   bool is_mapping_identity() const { return identity_mapping_; }
indices_map_size()64   size_t indices_map_size() const {
65     if (is_mapping_identity()) {
66       return 0;
67     }
68     return indices_map_.size();
69   }
70 
GetAddressOfMappedIndex(PointIndex point_index)71   const uint8_t *GetAddressOfMappedIndex(PointIndex point_index) const {
72     return GetAddress(mapped_index(point_index));
73   }
74 
75   // Sets the new number of unique attribute entries for the attribute. The
76   // function resizes the attribute storage to hold |num_attribute_values|
77   // entries.
78   // All previous entries with AttributeValueIndex < |num_attribute_values|
79   // are preserved. Caller needs to ensure that the PointAttribute is still
80   // valid after the resizing operation (that is, each point is mapped to a
81   // valid attribute value).
82   void Resize(size_t new_num_unique_entries);
83 
84   // Functions for setting the type of mapping between point indices and
85   // attribute entry ids.
86   // This function sets the mapping to implicit, where point indices are equal
87   // to attribute entry indices.
SetIdentityMapping()88   void SetIdentityMapping() {
89     identity_mapping_ = true;
90     indices_map_.clear();
91   }
92   // This function sets the mapping to be explicitly using the indices_map_
93   // array that needs to be initialized by the caller.
SetExplicitMapping(size_t num_points)94   void SetExplicitMapping(size_t num_points) {
95     identity_mapping_ = false;
96     indices_map_.resize(num_points, kInvalidAttributeValueIndex);
97   }
98 
99   // Set an explicit map entry for a specific point index.
SetPointMapEntry(PointIndex point_index,AttributeValueIndex entry_index)100   void SetPointMapEntry(PointIndex point_index,
101                         AttributeValueIndex entry_index) {
102     DRACO_DCHECK(!identity_mapping_);
103     indices_map_[point_index] = entry_index;
104   }
105 
106   // Same as GeometryAttribute::GetValue(), but using point id as the input.
107   // Mapping to attribute value index is performed automatically.
GetMappedValue(PointIndex point_index,void * out_data)108   void GetMappedValue(PointIndex point_index, void *out_data) const {
109     return GetValue(mapped_index(point_index), out_data);
110   }
111 
112 #ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
113   // Deduplicate |in_att| values into |this| attribute. |in_att| can be equal
114   // to |this|.
115   // Returns -1 if the deduplication failed.
116   AttributeValueIndex::ValueType DeduplicateValues(
117       const GeometryAttribute &in_att);
118 
119   // Same as above but the values read from |in_att| are sampled with the
120   // provided offset |in_att_offset|.
121   AttributeValueIndex::ValueType DeduplicateValues(
122       const GeometryAttribute &in_att, AttributeValueIndex in_att_offset);
123 #endif
124 
125   // Set attribute transform data for the attribute. The data is used to store
126   // the type and parameters of the transform that is applied on the attribute
127   // data (optional).
SetAttributeTransformData(std::unique_ptr<AttributeTransformData> transform_data)128   void SetAttributeTransformData(
129       std::unique_ptr<AttributeTransformData> transform_data) {
130     attribute_transform_data_ = std::move(transform_data);
131   }
GetAttributeTransformData()132   const AttributeTransformData *GetAttributeTransformData() const {
133     return attribute_transform_data_.get();
134   }
135 
136  private:
137 #ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
138   template <typename T>
139   AttributeValueIndex::ValueType DeduplicateTypedValues(
140       const GeometryAttribute &in_att, AttributeValueIndex in_att_offset);
141   template <typename T, int COMPONENTS_COUNT>
142   AttributeValueIndex::ValueType DeduplicateFormattedValues(
143       const GeometryAttribute &in_att, AttributeValueIndex in_att_offset);
144 #endif
145 
146   // Data storage for attribute values. GeometryAttribute itself doesn't own its
147   // buffer so we need to allocate it here.
148   std::unique_ptr<DataBuffer> attribute_buffer_;
149 
150   // Mapping between point ids and attribute value ids.
151   IndexTypeVector<PointIndex, AttributeValueIndex> indices_map_;
152   AttributeValueIndex::ValueType num_unique_entries_;
153   // Flag when the mapping between point ids and attribute values is identity.
154   bool identity_mapping_;
155 
156   // If an attribute contains transformed data (e.g. quantized), we can specify
157   // the attribute transform here and use it to transform the attribute back to
158   // its original format.
159   std::unique_ptr<AttributeTransformData> attribute_transform_data_;
160 
161   friend struct PointAttributeHasher;
162 };
163 
164 // Hash functor for the PointAttribute class.
165 struct PointAttributeHasher {
operatorPointAttributeHasher166   size_t operator()(const PointAttribute &attribute) const {
167     GeometryAttributeHasher base_hasher;
168     size_t hash = base_hasher(attribute);
169     hash = HashCombine(attribute.identity_mapping_, hash);
170     hash = HashCombine(attribute.num_unique_entries_, hash);
171     hash = HashCombine(attribute.indices_map_.size(), hash);
172     if (!attribute.indices_map_.empty()) {
173       const uint64_t indices_hash = FingerprintString(
174           reinterpret_cast<const char *>(attribute.indices_map_.data()),
175           attribute.indices_map_.size());
176       hash = HashCombine(indices_hash, hash);
177     }
178     if (attribute.attribute_buffer_ != nullptr) {
179       const uint64_t buffer_hash = FingerprintString(
180           reinterpret_cast<const char *>(attribute.attribute_buffer_->data()),
181           attribute.attribute_buffer_->data_size());
182       hash = HashCombine(buffer_hash, hash);
183     }
184     return hash;
185   }
186 };
187 
188 }  // namespace draco
189 
190 #endif  // DRACO_ATTRIBUTES_POINT_ATTRIBUTE_H_
191