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