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_GEOMETRY_ATTRIBUTE_H_
16 #define DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_
17 
18 #include <array>
19 #include <limits>
20 
21 #include "draco/attributes/geometry_indices.h"
22 #include "draco/core/data_buffer.h"
23 #include "draco/core/hash_utils.h"
24 
25 namespace draco {
26 
27 // The class provides access to a specific attribute which is stored in a
28 // DataBuffer, such as normals or coordinates. However, the GeometryAttribute
29 // class does not own the buffer and the buffer itself may store other data
30 // unrelated to this attribute (such as data for other attributes in which case
31 // we can have multiple GeometryAttributes accessing one buffer). Typically,
32 // all attributes for a point (or corner, face) are stored in one block, which
33 // is advantageous in terms of memory access. The length of the entire block is
34 // given by the byte_stride, the position where the attribute starts is given by
35 // the byte_offset, the actual number of bytes that the attribute occupies is
36 // given by the data_type and the number of components.
37 class GeometryAttribute {
38  public:
39   // Supported attribute types.
40   enum Type {
41     INVALID = -1,
42     // Named attributes start here. The difference between named and generic
43     // attributes is that for named attributes we know their purpose and we
44     // can apply some special methods when dealing with them (e.g. during
45     // encoding).
46     POSITION = 0,
47     NORMAL,
48     COLOR,
49     TEX_COORD,
50     // A special id used to mark attributes that are not assigned to any known
51     // predefined use case. Such attributes are often used for a shader specific
52     // data.
53     GENERIC,
54     // Total number of different attribute types.
55     // Always keep behind all named attributes.
56     NAMED_ATTRIBUTES_COUNT,
57   };
58 
59   GeometryAttribute();
60   // Initializes and enables the attribute.
61   void Init(Type attribute_type, DataBuffer *buffer, int8_t num_components,
62             DataType data_type, bool normalized, int64_t byte_stride,
63             int64_t byte_offset);
IsValid()64   bool IsValid() const { return buffer_ != nullptr; }
65 
66   // Copies data from the source attribute to the this attribute.
67   // This attribute must have a valid buffer allocated otherwise the operation
68   // is going to fail and return false.
69   bool CopyFrom(const GeometryAttribute &src_att);
70 
71   // Function for getting a attribute value with a specific format.
72   // Unsafe. Caller must ensure the accessed memory is valid.
73   // T is the attribute data type.
74   // att_components_t is the number of attribute components.
75   template <typename T, int att_components_t>
GetValue(AttributeValueIndex att_index)76   std::array<T, att_components_t> GetValue(
77       AttributeValueIndex att_index) const {
78     // Byte address of the attribute index.
79     const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
80     std::array<T, att_components_t> out;
81     buffer_->Read(byte_pos, &(out[0]), sizeof(out));
82     return out;
83   }
84 
85   // Function for getting a attribute value with a specific format.
86   // T is the attribute data type.
87   // att_components_t is the number of attribute components.
88   template <typename T, int att_components_t>
GetValue(AttributeValueIndex att_index,std::array<T,att_components_t> * out)89   bool GetValue(AttributeValueIndex att_index,
90                 std::array<T, att_components_t> *out) const {
91     // Byte address of the attribute index.
92     const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
93     // Check we are not reading past end of data.
94     if (byte_pos + sizeof(*out) > buffer_->data_size()) {
95       return false;
96     }
97     buffer_->Read(byte_pos, &((*out)[0]), sizeof(*out));
98     return true;
99   }
100 
101   // Returns the byte position of the attribute entry in the data buffer.
GetBytePos(AttributeValueIndex att_index)102   inline int64_t GetBytePos(AttributeValueIndex att_index) const {
103     return byte_offset_ + byte_stride_ * att_index.value();
104   }
105 
GetAddress(AttributeValueIndex att_index)106   inline const uint8_t *GetAddress(AttributeValueIndex att_index) const {
107     const int64_t byte_pos = GetBytePos(att_index);
108     return buffer_->data() + byte_pos;
109   }
GetAddress(AttributeValueIndex att_index)110   inline uint8_t *GetAddress(AttributeValueIndex att_index) {
111     const int64_t byte_pos = GetBytePos(att_index);
112     return buffer_->data() + byte_pos;
113   }
IsAddressValid(const uint8_t * address)114   inline bool IsAddressValid(const uint8_t *address) const {
115     return ((buffer_->data() + buffer_->data_size()) > address);
116   }
117 
118   // Fills out_data with the raw value of the requested attribute entry.
119   // out_data must be at least byte_stride_ long.
GetValue(AttributeValueIndex att_index,void * out_data)120   void GetValue(AttributeValueIndex att_index, void *out_data) const {
121     const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
122     buffer_->Read(byte_pos, out_data, byte_stride_);
123   }
124 
125   // Sets a value of an attribute entry. The input value must be allocated to
126   // cover all components of a single attribute entry.
SetAttributeValue(AttributeValueIndex entry_index,const void * value)127   void SetAttributeValue(AttributeValueIndex entry_index, const void *value) {
128     const int64_t byte_pos = entry_index.value() * byte_stride();
129     buffer_->Write(byte_pos, value, byte_stride());
130   }
131 
132   // DEPRECATED: Use
133   //   ConvertValue(AttributeValueIndex att_id,
134   //               int out_num_components,
135   //               OutT *out_val);
136   //
137   // Function for conversion of a attribute to a specific output format.
138   // OutT is the desired data type of the attribute.
139   // out_att_components_t is the number of components of the output format.
140   // Returns false when the conversion failed.
141   template <typename OutT, int out_att_components_t>
ConvertValue(AttributeValueIndex att_id,OutT * out_val)142   bool ConvertValue(AttributeValueIndex att_id, OutT *out_val) const {
143     return ConvertValue(att_id, out_att_components_t, out_val);
144   }
145 
146   // Function for conversion of a attribute to a specific output format.
147   // |out_val| needs to be able to store |out_num_components| values.
148   // OutT is the desired data type of the attribute.
149   // Returns false when the conversion failed.
150   template <typename OutT>
ConvertValue(AttributeValueIndex att_id,int8_t out_num_components,OutT * out_val)151   bool ConvertValue(AttributeValueIndex att_id, int8_t out_num_components,
152                     OutT *out_val) const {
153     if (out_val == nullptr) {
154       return false;
155     }
156     switch (data_type_) {
157       case DT_INT8:
158         return ConvertTypedValue<int8_t, OutT>(att_id, out_num_components,
159                                                out_val);
160       case DT_UINT8:
161         return ConvertTypedValue<uint8_t, OutT>(att_id, out_num_components,
162                                                 out_val);
163       case DT_INT16:
164         return ConvertTypedValue<int16_t, OutT>(att_id, out_num_components,
165                                                 out_val);
166       case DT_UINT16:
167         return ConvertTypedValue<uint16_t, OutT>(att_id, out_num_components,
168                                                  out_val);
169       case DT_INT32:
170         return ConvertTypedValue<int32_t, OutT>(att_id, out_num_components,
171                                                 out_val);
172       case DT_UINT32:
173         return ConvertTypedValue<uint32_t, OutT>(att_id, out_num_components,
174                                                  out_val);
175       case DT_INT64:
176         return ConvertTypedValue<int64_t, OutT>(att_id, out_num_components,
177                                                 out_val);
178       case DT_UINT64:
179         return ConvertTypedValue<uint64_t, OutT>(att_id, out_num_components,
180                                                  out_val);
181       case DT_FLOAT32:
182         return ConvertTypedValue<float, OutT>(att_id, out_num_components,
183                                               out_val);
184       case DT_FLOAT64:
185         return ConvertTypedValue<double, OutT>(att_id, out_num_components,
186                                                out_val);
187       case DT_BOOL:
188         return ConvertTypedValue<bool, OutT>(att_id, out_num_components,
189                                              out_val);
190       default:
191         // Wrong attribute type.
192         return false;
193     }
194   }
195 
196   // Function for conversion of a attribute to a specific output format.
197   // The |out_value| must be able to store all components of a single attribute
198   // entry.
199   // OutT is the desired data type of the attribute.
200   // Returns false when the conversion failed.
201   template <typename OutT>
ConvertValue(AttributeValueIndex att_index,OutT * out_value)202   bool ConvertValue(AttributeValueIndex att_index, OutT *out_value) const {
203     return ConvertValue<OutT>(att_index, num_components_, out_value);
204   }
205 
206   // Utility function. Returns |attribute_type| as std::string.
TypeToString(Type attribute_type)207   static std::string TypeToString(Type attribute_type) {
208     switch (attribute_type) {
209       case INVALID:
210         return "INVALID";
211       case POSITION:
212         return "POSITION";
213       case NORMAL:
214         return "NORMAL";
215       case COLOR:
216         return "COLOR";
217       case TEX_COORD:
218         return "TEX_COORD";
219       case GENERIC:
220         return "GENERIC";
221       default:
222         return "UNKNOWN";
223     }
224   }
225 
226   bool operator==(const GeometryAttribute &va) const;
227 
228   // Returns the type of the attribute indicating the nature of the attribute.
attribute_type()229   Type attribute_type() const { return attribute_type_; }
set_attribute_type(Type type)230   void set_attribute_type(Type type) { attribute_type_ = type; }
231   // Returns the data type that is stored in the attribute.
data_type()232   DataType data_type() const { return data_type_; }
233   // Returns the number of components that are stored for each entry.
234   // For position attribute this is usually three (x,y,z),
235   // while texture coordinates have two components (u,v).
num_components()236   int8_t num_components() const { return num_components_; }
237   // Indicates whether the data type should be normalized before interpretation,
238   // that is, it should be divided by the max value of the data type.
normalized()239   bool normalized() const { return normalized_; }
240   // The buffer storing the entire data of the attribute.
buffer()241   const DataBuffer *buffer() const { return buffer_; }
242   // Returns the number of bytes between two attribute entries, this is, at
243   // least size of the data types times number of components.
byte_stride()244   int64_t byte_stride() const { return byte_stride_; }
245   // The offset where the attribute starts within the block of size byte_stride.
byte_offset()246   int64_t byte_offset() const { return byte_offset_; }
set_byte_offset(int64_t byte_offset)247   void set_byte_offset(int64_t byte_offset) { byte_offset_ = byte_offset; }
buffer_descriptor()248   DataBufferDescriptor buffer_descriptor() const { return buffer_descriptor_; }
unique_id()249   uint32_t unique_id() const { return unique_id_; }
set_unique_id(uint32_t id)250   void set_unique_id(uint32_t id) { unique_id_ = id; }
251 
252  protected:
253   // Sets a new internal storage for the attribute.
254   void ResetBuffer(DataBuffer *buffer, int64_t byte_stride,
255                    int64_t byte_offset);
256 
257  private:
258   // Function for conversion of an attribute to a specific output format given a
259   // format of the stored attribute.
260   // T is the stored attribute data type.
261   // OutT is the desired data type of the attribute.
262   template <typename T, typename OutT>
ConvertTypedValue(AttributeValueIndex att_id,int8_t out_num_components,OutT * out_value)263   bool ConvertTypedValue(AttributeValueIndex att_id, int8_t out_num_components,
264                          OutT *out_value) const {
265     const uint8_t *src_address = GetAddress(att_id);
266 
267     // Convert all components available in both the original and output formats.
268     for (int i = 0; i < std::min(num_components_, out_num_components); ++i) {
269       if (!IsAddressValid(src_address)) {
270         return false;
271       }
272       const T in_value = *reinterpret_cast<const T *>(src_address);
273 
274       // Make sure the in_value fits within the range of values that OutT
275       // is able to represent. Perform the check only for integral types.
276       if (std::is_integral<T>::value && std::is_integral<OutT>::value) {
277         static constexpr OutT kOutMin =
278             std::is_signed<T>::value ? std::numeric_limits<OutT>::lowest() : 0;
279         if (in_value < kOutMin || in_value > std::numeric_limits<OutT>::max()) {
280           return false;
281         }
282       }
283 
284       out_value[i] = static_cast<OutT>(in_value);
285       // When converting integer to floating point, normalize the value if
286       // necessary.
287       if (std::is_integral<T>::value && std::is_floating_point<OutT>::value &&
288           normalized_) {
289         out_value[i] /= static_cast<OutT>(std::numeric_limits<T>::max());
290       }
291       // TODO(ostava): Add handling of normalized attributes when converting
292       // between different integer representations. If the attribute is
293       // normalized, integer values should be converted as if they represent 0-1
294       // range. E.g. when we convert uint16 to uint8, the range <0, 2^16 - 1>
295       // should be converted to range <0, 2^8 - 1>.
296       src_address += sizeof(T);
297     }
298     // Fill empty data for unused output components if needed.
299     for (int i = num_components_; i < out_num_components; ++i) {
300       out_value[i] = static_cast<OutT>(0);
301     }
302     return true;
303   }
304 
305   DataBuffer *buffer_;
306   // The buffer descriptor is stored at the time the buffer is attached to this
307   // attribute. The purpose is to detect if any changes happened to the buffer
308   // since the time it was attached.
309   DataBufferDescriptor buffer_descriptor_;
310   int8_t num_components_;
311   DataType data_type_;
312   bool normalized_;
313   int64_t byte_stride_;
314   int64_t byte_offset_;
315 
316   Type attribute_type_;
317 
318   // Unique id of this attribute. No two attributes could have the same unique
319   // id. It is used to identify each attribute, especially when there are
320   // multiple attribute of the same type in a point cloud.
321   uint32_t unique_id_;
322 
323   friend struct GeometryAttributeHasher;
324 };
325 
326 // Hashing support
327 
328 // Function object for using Attribute as a hash key.
329 struct GeometryAttributeHasher {
operatorGeometryAttributeHasher330   size_t operator()(const GeometryAttribute &va) const {
331     size_t hash = HashCombine(va.buffer_descriptor_.buffer_id,
332                               va.buffer_descriptor_.buffer_update_count);
333     hash = HashCombine(va.num_components_, hash);
334     hash = HashCombine(static_cast<int8_t>(va.data_type_), hash);
335     hash = HashCombine(static_cast<int8_t>(va.attribute_type_), hash);
336     hash = HashCombine(va.byte_stride_, hash);
337     return HashCombine(va.byte_offset_, hash);
338   }
339 };
340 
341 // Function object for using GeometryAttribute::Type as a hash key.
342 struct GeometryAttributeTypeHasher {
operatorGeometryAttributeTypeHasher343   size_t operator()(const GeometryAttribute::Type &at) const {
344     return static_cast<size_t>(at);
345   }
346 };
347 
348 }  // namespace draco
349 
350 #endif  // DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_
351