GameApi 28
Library used by GameApi Builder tool
Loading...
Searching...
No Matches
geometry_attribute.h
Go to the documentation of this file.
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 <algorithm>
19#include <array>
20#include <cmath>
21#include <limits>
22
27#ifdef DRACO_TRANSCODER_SUPPORTED
28#include "draco/core/status.h"
29#endif
30
31namespace draco {
32
33// The class provides access to a specific attribute which is stored in a
34// DataBuffer, such as normals or coordinates. However, the GeometryAttribute
35// class does not own the buffer and the buffer itself may store other data
36// unrelated to this attribute (such as data for other attributes in which case
37// we can have multiple GeometryAttributes accessing one buffer). Typically,
38// all attributes for a point (or corner, face) are stored in one block, which
39// is advantageous in terms of memory access. The length of the entire block is
40// given by the byte_stride, the position where the attribute starts is given by
41// the byte_offset, the actual number of bytes that the attribute occupies is
42// given by the data_type and the number of components.
44 public:
45 // Supported attribute types.
46 enum Type {
47 INVALID = -1,
48 // Named attributes start here. The difference between named and generic
49 // attributes is that for named attributes we know their purpose and we
50 // can apply some special methods when dealing with them (e.g. during
51 // encoding).
56 // A special id used to mark attributes that are not assigned to any known
57 // predefined use case. Such attributes are often used for a shader specific
58 // data.
60#ifdef DRACO_TRANSCODER_SUPPORTED
61 // TODO(ostava): Adding a new attribute would be bit-stream change for GLTF.
62 // Older decoders wouldn't know what to do with this attribute type. This
63 // should be open-sourced only when we are ready to increase our bit-stream
64 // version.
65 TANGENT,
66 MATERIAL,
67 JOINTS,
68 WEIGHTS,
69#endif
70 // Total number of different attribute types.
71 // Always keep behind all named attributes.
73 };
74
76 // Initializes and enables the attribute.
79 int64_t byte_offset);
80 bool IsValid() const { return buffer_ != nullptr; }
81
82 // Copies data from the source attribute to the this attribute.
83 // This attribute must have a valid buffer allocated otherwise the operation
84 // is going to fail and return false.
85 bool CopyFrom(const GeometryAttribute &src_att);
86
87 // Function for getting a attribute value with a specific format.
88 // Unsafe. Caller must ensure the accessed memory is valid.
89 // T is the attribute data type.
90 // att_components_t is the number of attribute components.
91 template <typename T, int att_components_t>
92 std::array<T, att_components_t> GetValue(
93 AttributeValueIndex att_index) const {
94 // Byte address of the attribute index.
95 const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
96 std::array<T, att_components_t> out;
97 buffer_->Read(byte_pos, &(out[0]), sizeof(out));
98 return out;
99 }
100
101 // Function for getting a attribute value with a specific format.
102 // T is the attribute data type.
103 // att_components_t is the number of attribute components.
104 template <typename T, int att_components_t>
105 bool GetValue(AttributeValueIndex att_index,
106 std::array<T, att_components_t> *out) const {
107 // Byte address of the attribute index.
108 const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
109 // Check we are not reading past end of data.
110 if (byte_pos + sizeof(*out) > buffer_->data_size()) {
111 return false;
112 }
113 buffer_->Read(byte_pos, &((*out)[0]), sizeof(*out));
114 return true;
115 }
116
117 // Returns the byte position of the attribute entry in the data buffer.
118 inline int64_t GetBytePos(AttributeValueIndex att_index) const {
119 return byte_offset_ + byte_stride_ * att_index.value();
120 }
121
122 inline const uint8_t *GetAddress(AttributeValueIndex att_index) const {
123 const int64_t byte_pos = GetBytePos(att_index);
124 return buffer_->data() + byte_pos;
125 }
126 inline uint8_t *GetAddress(AttributeValueIndex att_index) {
127 const int64_t byte_pos = GetBytePos(att_index);
128 return buffer_->data() + byte_pos;
129 }
130 inline bool IsAddressValid(const uint8_t *address) const {
131 return ((buffer_->data() + buffer_->data_size()) > address);
132 }
133
134 // Fills out_data with the raw value of the requested attribute entry.
135 // out_data must be at least byte_stride_ long.
136 void GetValue(AttributeValueIndex att_index, void *out_data) const {
137 const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
138 buffer_->Read(byte_pos, out_data, byte_stride_);
139 }
140
141 // Sets a value of an attribute entry. The input value must be allocated to
142 // cover all components of a single attribute entry.
143 void SetAttributeValue(AttributeValueIndex entry_index, const void *value) {
144 const int64_t byte_pos = entry_index.value() * byte_stride();
145 buffer_->Write(byte_pos, value, byte_stride());
146 }
147
148#ifdef DRACO_TRANSCODER_SUPPORTED
149 // Sets a value of an attribute entry. The input |value| must have
150 // |input_num_components| entries and it will be automatically converted to
151 // the internal format used by the geometry attribute. If the conversion is
152 // not possible, an error status will be returned.
153 template <typename InputT>
154 Status ConvertAndSetAttributeValue(AttributeValueIndex avi,
155 int input_num_components,
156 const InputT *value);
157#endif
158
159 // DEPRECATED: Use
160 // ConvertValue(AttributeValueIndex att_id,
161 // int out_num_components,
162 // OutT *out_val);
163 //
164 // Function for conversion of a attribute to a specific output format.
165 // OutT is the desired data type of the attribute.
166 // out_att_components_t is the number of components of the output format.
167 // Returns false when the conversion failed.
168 template <typename OutT, int out_att_components_t>
169 bool ConvertValue(AttributeValueIndex att_id, OutT *out_val) const {
170 return ConvertValue(att_id, out_att_components_t, out_val);
171 }
172
173 // Function for conversion of a attribute to a specific output format.
174 // |out_val| needs to be able to store |out_num_components| values.
175 // OutT is the desired data type of the attribute.
176 // Returns false when the conversion failed.
177 template <typename OutT>
178 bool ConvertValue(AttributeValueIndex att_id, int8_t out_num_components,
179 OutT *out_val) const {
180 if (out_val == nullptr) {
181 return false;
182 }
183 switch (data_type_) {
184 case DT_INT8:
185 return ConvertTypedValue<int8_t, OutT>(att_id, out_num_components,
186 out_val);
187 case DT_UINT8:
188 return ConvertTypedValue<uint8_t, OutT>(att_id, out_num_components,
189 out_val);
190 case DT_INT16:
191 return ConvertTypedValue<int16_t, OutT>(att_id, out_num_components,
192 out_val);
193 case DT_UINT16:
194 return ConvertTypedValue<uint16_t, OutT>(att_id, out_num_components,
195 out_val);
196 case DT_INT32:
197 return ConvertTypedValue<int32_t, OutT>(att_id, out_num_components,
198 out_val);
199 case DT_UINT32:
200 return ConvertTypedValue<uint32_t, OutT>(att_id, out_num_components,
201 out_val);
202 case DT_INT64:
203 return ConvertTypedValue<int64_t, OutT>(att_id, out_num_components,
204 out_val);
205 case DT_UINT64:
206 return ConvertTypedValue<uint64_t, OutT>(att_id, out_num_components,
207 out_val);
208 case DT_FLOAT32:
209 return ConvertTypedValue<float, OutT>(att_id, out_num_components,
210 out_val);
211 case DT_FLOAT64:
212 return ConvertTypedValue<double, OutT>(att_id, out_num_components,
213 out_val);
214 case DT_BOOL:
215 return ConvertTypedValue<bool, OutT>(att_id, out_num_components,
216 out_val);
217 default:
218 // Wrong attribute type.
219 return false;
220 }
221 }
222
223 // Function for conversion of a attribute to a specific output format.
224 // The |out_value| must be able to store all components of a single attribute
225 // entry.
226 // OutT is the desired data type of the attribute.
227 // Returns false when the conversion failed.
228 template <typename OutT>
229 bool ConvertValue(AttributeValueIndex att_index, OutT *out_value) const {
230 return ConvertValue<OutT>(att_index, num_components_, out_value);
231 }
232
233 // Utility function. Returns |attribute_type| as std::string.
234 static std::string TypeToString(Type attribute_type) {
235 switch (attribute_type) {
236 case INVALID:
237 return "INVALID";
238 case POSITION:
239 return "POSITION";
240 case NORMAL:
241 return "NORMAL";
242 case COLOR:
243 return "COLOR";
244 case TEX_COORD:
245 return "TEX_COORD";
246 case GENERIC:
247 return "GENERIC";
248 default:
249 return "UNKNOWN";
250 }
251 }
252
253 bool operator==(const GeometryAttribute &va) const;
254
255 // Returns the type of the attribute indicating the nature of the attribute.
256 Type attribute_type() const { return attribute_type_; }
257 void set_attribute_type(Type type) { attribute_type_ = type; }
258 // Returns the data type that is stored in the attribute.
259 DataType data_type() const { return data_type_; }
260 // Returns the number of components that are stored for each entry.
261 // For position attribute this is usually three (x,y,z),
262 // while texture coordinates have two components (u,v).
263 uint8_t num_components() const { return num_components_; }
264 // Indicates whether the data type should be normalized before interpretation,
265 // that is, it should be divided by the max value of the data type.
266 bool normalized() const { return normalized_; }
267 void set_normalized(bool normalized) { normalized_ = normalized; }
268 // The buffer storing the entire data of the attribute.
269 const DataBuffer *buffer() const { return buffer_; }
270 // Returns the number of bytes between two attribute entries, this is, at
271 // least size of the data types times number of components.
272 int64_t byte_stride() const { return byte_stride_; }
273 // The offset where the attribute starts within the block of size byte_stride.
274 int64_t byte_offset() const { return byte_offset_; }
275 void set_byte_offset(int64_t byte_offset) { byte_offset_ = byte_offset; }
276 DataBufferDescriptor buffer_descriptor() const { return buffer_descriptor_; }
277 uint32_t unique_id() const { return unique_id_; }
278 void set_unique_id(uint32_t id) { unique_id_ = id; }
279
280 protected:
281 // Sets a new internal storage for the attribute.
283 int64_t byte_offset);
284
285 private:
286 // Function for conversion of an attribute to a specific output format given a
287 // format of the stored attribute.
288 // T is the stored attribute data type.
289 // OutT is the desired data type of the attribute.
290 template <typename T, typename OutT>
291 bool ConvertTypedValue(AttributeValueIndex att_id, uint8_t out_num_components,
292 OutT *out_value) const {
293 const uint8_t *src_address = GetAddress(att_id);
294
295 // Convert all components available in both the original and output formats.
296 for (int i = 0; i < std::min(num_components_, out_num_components); ++i) {
297 if (!IsAddressValid(src_address)) {
298 return false;
299 }
300 const T in_value = *reinterpret_cast<const T *>(src_address);
301 if (!ConvertComponentValue<T, OutT>(in_value, normalized_,
302 out_value + i)) {
303 return false;
304 }
305 src_address += sizeof(T);
306 }
307 // Fill empty data for unused output components if needed.
308 for (int i = num_components_; i < out_num_components; ++i) {
309 out_value[i] = static_cast<OutT>(0);
310 }
311 return true;
312 }
313
314#ifdef DRACO_TRANSCODER_SUPPORTED
315 // Function that converts input |value| from type T to the internal attribute
316 // representation defined by OutT and |num_components_|.
317 template <typename T, typename OutT>
318 Status ConvertAndSetAttributeTypedValue(AttributeValueIndex avi,
319 int8_t input_num_components,
320 const T *value) {
321 uint8_t *address = GetAddress(avi);
322
323 // Convert all components available in both the original and output formats.
324 for (int i = 0; i < num_components_; ++i) {
325 if (!IsAddressValid(address)) {
326 return ErrorStatus("GeometryAttribute: Invalid address.");
327 }
328 OutT *const out_value = reinterpret_cast<OutT *>(address);
329 if (i < input_num_components) {
330 if (!ConvertComponentValue<T, OutT>(*(value + i), normalized_,
331 out_value)) {
332 return ErrorStatus(
333 "GeometryAttribute: Failed to convert component value.");
334 }
335 } else {
336 *out_value = static_cast<OutT>(0);
337 }
338 address += sizeof(OutT);
339 }
340 return OkStatus();
341 }
342#endif // DRACO_TRANSCODER_SUPPORTED
343
344 // Converts |in_value| of type T into |out_value| of type OutT. If
345 // |normalized| is true, any conversion between floating point and integer
346 // values will be treating integers as normalized types (the entire integer
347 // range will be used to represent 0-1 floating point range).
348 template <typename T, typename OutT>
349 static bool ConvertComponentValue(const T &in_value, bool normalized,
350 OutT *out_value) {
351 // Make sure the |in_value| can be represented as an integral type OutT.
352 if (std::is_integral<OutT>::value) {
353 // Make sure the |in_value| fits within the range of values that OutT
354 // is able to represent. Perform the check only for integral types.
355 if (!std::is_same<T, bool>::value && std::is_integral<T>::value) {
356 static constexpr OutT kOutMin =
357 std::is_signed<T>::value ? std::numeric_limits<OutT>::min() : 0;
358 if (in_value < kOutMin || in_value > std::numeric_limits<OutT>::max()) {
359 return false;
360 }
361 }
362
363 // Check conversion of floating point |in_value| to integral value OutT.
364 if (std::is_floating_point<T>::value) {
365 // Make sure the floating point |in_value| is not NaN and not Inf as
366 // integral type OutT is unable to represent these values.
367 if (sizeof(in_value) > sizeof(double)) {
368 if (std::isnan(static_cast<long double>(in_value)) ||
369 std::isinf(static_cast<long double>(in_value))) {
370 return false;
371 }
372 } else if (sizeof(in_value) > sizeof(float)) {
373 if (std::isnan(static_cast<double>(in_value)) ||
374 std::isinf(static_cast<double>(in_value))) {
375 return false;
376 }
377 } else {
378 if (std::isnan(static_cast<float>(in_value)) ||
379 std::isinf(static_cast<float>(in_value))) {
380 return false;
381 }
382 }
383
384 // Make sure the floating point |in_value| fits within the range of
385 // values that integral type OutT is able to represent.
386 if (in_value < std::numeric_limits<OutT>::min() ||
387 in_value >= std::numeric_limits<OutT>::max()) {
388 return false;
389 }
390 }
391 }
392
393 if (std::is_integral<T>::value && std::is_floating_point<OutT>::value &&
394 normalized) {
395 // When converting integer to floating point, normalize the value if
396 // necessary.
397 *out_value = static_cast<OutT>(in_value);
398 *out_value /= static_cast<OutT>(std::numeric_limits<T>::max());
399 } else if (std::is_floating_point<T>::value &&
400 std::is_integral<OutT>::value && normalized) {
401 // Converting from floating point to a normalized integer.
402 if (in_value > 1 || in_value < 0) {
403 // Normalized float values need to be between 0 and 1.
404 return false;
405 }
406 // TODO(ostava): Consider allowing float to normalized integer conversion
407 // for 64-bit integer types. Currently it doesn't work because we don't
408 // have a floating point type that could store all 64 bit integers.
409 if (sizeof(OutT) > 4) {
410 return false;
411 }
412 // Expand the float to the range of the output integer and round it to the
413 // nearest representable value. Use doubles for the math to ensure the
414 // integer values are represented properly during the conversion process.
415 *out_value = static_cast<OutT>(std::floor(
416 in_value * static_cast<double>(std::numeric_limits<OutT>::max()) +
417 0.5));
418 } else {
419 *out_value = static_cast<OutT>(in_value);
420 }
421
422 // TODO(ostava): Add handling of normalized attributes when converting
423 // between different integer representations. If the attribute is
424 // normalized, integer values should be converted as if they represent 0-1
425 // range. E.g. when we convert uint16 to uint8, the range <0, 2^16 - 1>
426 // should be converted to range <0, 2^8 - 1>.
427 return true;
428 }
429
430 DataBuffer *buffer_;
431 // The buffer descriptor is stored at the time the buffer is attached to this
432 // attribute. The purpose is to detect if any changes happened to the buffer
433 // since the time it was attached.
434 DataBufferDescriptor buffer_descriptor_;
435 uint8_t num_components_;
436 DataType data_type_;
437 bool normalized_;
438 int64_t byte_stride_;
439 int64_t byte_offset_;
440
441 Type attribute_type_;
442
443 // Unique id of this attribute. No two attributes could have the same unique
444 // id. It is used to identify each attribute, especially when there are
445 // multiple attribute of the same type in a point cloud.
446 uint32_t unique_id_;
447
449};
450
451#ifdef DRACO_TRANSCODER_SUPPORTED
452template <typename InputT>
453Status GeometryAttribute::ConvertAndSetAttributeValue(AttributeValueIndex avi,
454 int input_num_components,
455 const InputT *value) {
456 switch (this->data_type()) {
457 case DT_INT8:
458 return ConvertAndSetAttributeTypedValue<InputT, int8_t>(
459 avi, input_num_components, value);
460 case DT_UINT8:
461 return ConvertAndSetAttributeTypedValue<InputT, uint8_t>(
462 avi, input_num_components, value);
463 case DT_INT16:
464 return ConvertAndSetAttributeTypedValue<InputT, int16_t>(
465 avi, input_num_components, value);
466 case DT_UINT16:
467 return ConvertAndSetAttributeTypedValue<InputT, uint16_t>(
468 avi, input_num_components, value);
469 case DT_INT32:
470 return ConvertAndSetAttributeTypedValue<InputT, int32_t>(
471 avi, input_num_components, value);
472 case DT_UINT32:
473 return ConvertAndSetAttributeTypedValue<InputT, uint32_t>(
474 avi, input_num_components, value);
475 case DT_INT64:
476 return ConvertAndSetAttributeTypedValue<InputT, int64_t>(
477 avi, input_num_components, value);
478 case DT_UINT64:
479 return ConvertAndSetAttributeTypedValue<InputT, uint64_t>(
480 avi, input_num_components, value);
481 case DT_FLOAT32:
482 return ConvertAndSetAttributeTypedValue<InputT, float>(
483 avi, input_num_components, value);
484 case DT_FLOAT64:
485 return ConvertAndSetAttributeTypedValue<InputT, double>(
486 avi, input_num_components, value);
487 case DT_BOOL:
488 return ConvertAndSetAttributeTypedValue<InputT, bool>(
489 avi, input_num_components, value);
490 default:
491 break;
492 }
493 return ErrorStatus(
494 "GeometryAttribute::SetAndConvertAttributeValue: Unsupported "
495 "attribute type.");
496}
497#endif
498
499// Hashing support
500
501// Function object for using Attribute as a hash key.
503 size_t operator()(const GeometryAttribute &va) const {
504 size_t hash = HashCombine(va.buffer_descriptor_.buffer_id,
505 va.buffer_descriptor_.buffer_update_count);
506 hash = HashCombine(va.num_components_, hash);
507 hash = HashCombine(static_cast<int8_t>(va.data_type_), hash);
508 hash = HashCombine(static_cast<int8_t>(va.attribute_type_), hash);
509 hash = HashCombine(va.byte_stride_, hash);
510 return HashCombine(va.byte_offset_, hash);
511 }
512};
513
514// Function object for using GeometryAttribute::Type as a hash key.
516 size_t operator()(const GeometryAttribute::Type &at) const {
517 return static_cast<size_t>(at);
518 }
519};
520
521} // namespace draco
522
523#endif // DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_
Type
Definition Database.hh:9
Definition data_buffer.h:36
Definition geometry_attribute.h:43
const DataBuffer * buffer() const
Definition geometry_attribute.h:269
bool IsAddressValid(const uint8_t *address) const
Definition geometry_attribute.h:130
uint32_t unique_id() const
Definition geometry_attribute.h:277
bool GetValue(AttributeValueIndex att_index, std::array< T, att_components_t > *out) const
Definition geometry_attribute.h:105
uint8_t num_components() const
Definition geometry_attribute.h:263
DataBufferDescriptor buffer_descriptor() const
Definition geometry_attribute.h:276
bool operator==(const GeometryAttribute &va) const
Definition geometry_attribute.cc:65
void set_normalized(bool normalized)
Definition geometry_attribute.h:267
bool IsValid() const
Definition geometry_attribute.h:80
int64_t byte_stride() const
Definition geometry_attribute.h:272
void set_unique_id(uint32_t id)
Definition geometry_attribute.h:278
void set_attribute_type(Type type)
Definition geometry_attribute.h:257
bool CopyFrom(const GeometryAttribute &src_att)
Definition geometry_attribute.cc:45
bool ConvertValue(AttributeValueIndex att_index, OutT *out_value) const
Definition geometry_attribute.h:229
Type attribute_type() const
Definition geometry_attribute.h:256
bool normalized() const
Definition geometry_attribute.h:266
friend struct GeometryAttributeHasher
Definition geometry_attribute.h:448
void Init(Type attribute_type, DataBuffer *buffer, uint8_t num_components, DataType data_type, bool normalized, int64_t byte_stride, int64_t byte_offset)
Definition geometry_attribute.cc:28
bool ConvertValue(AttributeValueIndex att_id, OutT *out_val) const
Definition geometry_attribute.h:169
void GetValue(AttributeValueIndex att_index, void *out_data) const
Definition geometry_attribute.h:136
void set_byte_offset(int64_t byte_offset)
Definition geometry_attribute.h:275
uint8_t * GetAddress(AttributeValueIndex att_index)
Definition geometry_attribute.h:126
void ResetBuffer(DataBuffer *buffer, int64_t byte_stride, int64_t byte_offset)
Definition geometry_attribute.cc:93
bool ConvertValue(AttributeValueIndex att_id, int8_t out_num_components, OutT *out_val) const
Definition geometry_attribute.h:178
int64_t GetBytePos(AttributeValueIndex att_index) const
Definition geometry_attribute.h:118
std::array< T, att_components_t > GetValue(AttributeValueIndex att_index) const
Definition geometry_attribute.h:92
void SetAttributeValue(AttributeValueIndex entry_index, const void *value)
Definition geometry_attribute.h:143
static std::string TypeToString(Type attribute_type)
Definition geometry_attribute.h:234
const uint8_t * GetAddress(AttributeValueIndex att_index) const
Definition geometry_attribute.h:122
int64_t byte_offset() const
Definition geometry_attribute.h:274
DataType data_type() const
Definition geometry_attribute.h:259
GeometryAttribute()
Definition geometry_attribute.cc:19
Type
Definition geometry_attribute.h:46
@ INVALID
Definition geometry_attribute.h:47
@ TEX_COORD
Definition geometry_attribute.h:55
@ GENERIC
Definition geometry_attribute.h:59
@ POSITION
Definition geometry_attribute.h:52
@ NAMED_ATTRIBUTES_COUNT
Definition geometry_attribute.h:72
@ COLOR
Definition geometry_attribute.h:54
@ NORMAL
Definition geometry_attribute.h:53
Definition status.h:25
Definition attribute_octahedron_transform.cc:21
size_t HashCombine(T1 a, T2 b)
Definition hash_utils.h:26
Status OkStatus()
Definition status.h:66
Status ErrorStatus(const std::string &msg)
Definition status.h:67
DataType
Definition draco_types.h:26
@ DT_INT32
Definition draco_types.h:33
@ DT_INT64
Definition draco_types.h:35
@ DT_INT8
Definition draco_types.h:29
@ DT_BOOL
Definition draco_types.h:39
@ DT_INT16
Definition draco_types.h:31
@ DT_UINT16
Definition draco_types.h:32
@ DT_UINT8
Definition draco_types.h:30
@ DT_FLOAT64
Definition draco_types.h:38
@ DT_FLOAT32
Definition draco_types.h:37
@ DT_UINT32
Definition draco_types.h:34
@ DT_UINT64
Definition draco_types.h:36
Definition GameApi.h:42
Definition data_buffer.h:27
int64_t buffer_id
Definition data_buffer.h:30
int64_t buffer_update_count
Definition data_buffer.h:32
Definition geometry_attribute.h:502
size_t operator()(const GeometryAttribute &va) const
Definition geometry_attribute.h:503
Definition geometry_attribute.h:515
size_t operator()(const GeometryAttribute::Type &at) const
Definition geometry_attribute.h:516