cesium-native  0.41.0
PropertyTexturePropertyView.h
1 #pragma once
2 
3 #include "CesiumGltf/ImageAsset.h"
4 #include "CesiumGltf/KhrTextureTransform.h"
5 #include "CesiumGltf/PropertyTextureProperty.h"
6 #include "CesiumGltf/PropertyTransformations.h"
7 #include "CesiumGltf/PropertyTypeTraits.h"
8 #include "CesiumGltf/PropertyView.h"
9 #include "CesiumGltf/Sampler.h"
10 #include "CesiumGltf/TextureView.h"
11 
12 #include <CesiumUtility/Assert.h>
13 
14 #include <array>
15 #include <cmath>
16 #include <cstdint>
17 #include <optional>
18 
19 namespace CesiumGltf {
29 public:
34  static const int ErrorInvalidPropertyTexture = 14;
35 
40  static const int ErrorUnsupportedProperty = 15;
41 
45  static const int ErrorInvalidTexture = 16;
46 
50  static const int ErrorInvalidSampler = 17;
51 
55  static const int ErrorInvalidImage = 18;
56 
60  static const int ErrorEmptyImage = 19;
61 
66  static const int ErrorInvalidBytesPerChannel = 20;
67 
75  static const int ErrorInvalidChannels = 21;
76 
83  static const int ErrorChannelsAndTypeMismatch = 22;
84 };
85 
86 template <typename ElementType>
87 ElementType assembleScalarValue(const gsl::span<uint8_t> bytes) noexcept {
88  if constexpr (std::is_same_v<ElementType, float>) {
89  CESIUM_ASSERT(
90  bytes.size() == sizeof(float) &&
91  "Not enough channel inputs to construct a float.");
92  uint32_t resultAsUint = 0;
93  for (size_t i = 0; i < bytes.size(); i++) {
94  resultAsUint |= static_cast<uint32_t>(bytes[i]) << i * 8;
95  }
96 
97  // Reinterpret the bits as a float.
98  return *reinterpret_cast<float*>(&resultAsUint);
99  }
100 
101  if constexpr (IsMetadataInteger<ElementType>::value) {
102  using UintType = std::make_unsigned_t<ElementType>;
103  UintType resultAsUint = 0;
104  for (size_t i = 0; i < bytes.size(); i++) {
105  resultAsUint |= static_cast<UintType>(bytes[i]) << i * 8;
106  }
107 
108  // Reinterpret the bits with the correct signedness.
109  return *reinterpret_cast<ElementType*>(&resultAsUint);
110  }
111 }
112 
113 template <typename ElementType>
114 ElementType assembleVecNValue(const gsl::span<uint8_t> bytes) noexcept {
115  ElementType result = ElementType();
116 
117  const glm::length_t N =
118  getDimensionsFromPropertyType(TypeToPropertyType<ElementType>::value);
119  using T = typename ElementType::value_type;
120 
121  CESIUM_ASSERT(
122  sizeof(T) <= 2 && "Components cannot be larger than two bytes in size.");
123 
124  if constexpr (std::is_same_v<T, int16_t>) {
125  CESIUM_ASSERT(
126  N == 2 && "Only vec2s can contain two-byte integer components.");
127  uint16_t x = static_cast<uint16_t>(bytes[0]) |
128  (static_cast<uint16_t>(bytes[1]) << 8);
129  uint16_t y = static_cast<uint16_t>(bytes[2]) |
130  (static_cast<uint16_t>(bytes[3]) << 8);
131 
132  result[0] = *reinterpret_cast<int16_t*>(&x);
133  result[1] = *reinterpret_cast<int16_t*>(&y);
134  }
135 
136  if constexpr (std::is_same_v<T, uint16_t>) {
137  CESIUM_ASSERT(
138  N == 2 && "Only vec2s can contain two-byte integer components.");
139  result[0] = static_cast<uint16_t>(bytes[0]) |
140  (static_cast<uint16_t>(bytes[1]) << 8);
141  result[1] = static_cast<uint16_t>(bytes[2]) |
142  (static_cast<uint16_t>(bytes[3]) << 8);
143  }
144 
145  if constexpr (std::is_same_v<T, int8_t>) {
146  for (size_t i = 0; i < bytes.size(); i++) {
147  result[i] = *reinterpret_cast<const int8_t*>(&bytes[i]);
148  }
149  }
150 
151  if constexpr (std::is_same_v<T, uint8_t>) {
152  for (size_t i = 0; i < bytes.size(); i++) {
153  result[i] = bytes[i];
154  }
155  }
156 
157  return result;
158 }
159 
160 template <typename T>
161 PropertyArrayCopy<T>
162 assembleArrayValue(const gsl::span<uint8_t> bytes) noexcept {
163  std::vector<T> result(bytes.size() / sizeof(T));
164 
165  if constexpr (sizeof(T) == 2) {
166  for (int i = 0, b = 0; i < result.size(); i++, b += 2) {
167  using UintType = std::make_unsigned_t<T>;
168  UintType resultAsUint = static_cast<UintType>(bytes[b]) |
169  (static_cast<UintType>(bytes[b + 1]) << 8);
170  result[i] = *reinterpret_cast<T*>(&resultAsUint);
171  }
172  } else {
173  for (size_t i = 0; i < bytes.size(); i++) {
174  result[i] = *reinterpret_cast<const T*>(&bytes[i]);
175  }
176  }
177 
178  return PropertyArrayCopy<T>(std::move(result));
179 }
180 
181 template <typename ElementType>
182 PropertyValueViewToCopy<ElementType>
183 assembleValueFromChannels(const gsl::span<uint8_t> bytes) noexcept {
184  CESIUM_ASSERT(
185  bytes.size() > 0 && "Channel input must have at least one value.");
186 
187  if constexpr (IsMetadataScalar<ElementType>::value) {
188  return assembleScalarValue<ElementType>(bytes);
189  }
190 
191  if constexpr (IsMetadataVecN<ElementType>::value) {
192  return assembleVecNValue<ElementType>(bytes);
193  }
194 
195  if constexpr (IsMetadataArray<ElementType>::value) {
196  return assembleArrayValue<typename MetadataArrayType<ElementType>::type>(
197  bytes);
198  }
199 }
200 
201 #pragma region Non - normalized property
202 
216 template <typename ElementType, bool Normalized = false>
218 
228 template <typename ElementType>
229 class PropertyTexturePropertyView<ElementType, false>
230  : public PropertyView<ElementType, false>, public TextureView {
231 public:
236  : PropertyView<ElementType, false>(),
237  TextureView(),
238  _channels(),
239  _swizzle() {}
240 
246  PropertyTexturePropertyView(PropertyViewStatusType status) noexcept
248  TextureView(),
249  _channels(),
250  _swizzle() {
251  CESIUM_ASSERT(
252  this->_status != PropertyTexturePropertyViewStatus::Valid &&
253  "An empty property view should not be constructed with a valid status");
254  }
255 
264  PropertyTexturePropertyView(const ClassProperty& classProperty) noexcept
265  : PropertyView<ElementType, false>(classProperty),
266  TextureView(),
267  _channels(),
268  _swizzle() {
269  if (this->_status != PropertyTexturePropertyViewStatus::Valid) {
270  // Don't override the status / size if something is wrong with the class
271  // property's definition.
272  return;
273  }
274 
275  if (!classProperty.defaultProperty) {
276  // This constructor should only be called if the class property *has* a
277  // default value. But in the case that it does not, this property view
278  // becomes invalid.
279  this->_status =
281  return;
282  }
283 
285  }
286 
298  const PropertyTextureProperty& property,
299  const ClassProperty& classProperty,
300  const Sampler& sampler,
301  const ImageAsset& image,
302  const TextureViewOptions& options = TextureViewOptions()) noexcept
303  : PropertyView<ElementType, false>(classProperty, property),
304  TextureView(
305  sampler,
306  image,
307  property.texCoord,
308  property.getExtension<ExtensionKhrTextureTransform>(),
309  options),
310  _channels(property.channels),
311  _swizzle() {
312  if (this->_status != PropertyTexturePropertyViewStatus::Valid) {
313  return;
314  }
315 
316  switch (this->getTextureViewStatus()) {
318  break;
321  return;
324  return;
327  return;
329  this->_status =
331  return;
334  default:
336  return;
337  }
338 
339  _swizzle.reserve(_channels.size());
340 
341  for (size_t i = 0; i < _channels.size(); ++i) {
342  switch (_channels[i]) {
343  case 0:
344  _swizzle += "r";
345  break;
346  case 1:
347  _swizzle += "g";
348  break;
349  case 2:
350  _swizzle += "b";
351  break;
352  case 3:
353  _swizzle += "a";
354  break;
355  default:
356  CESIUM_ASSERT(
357  false && "A valid channels vector must be passed to the view.");
358  }
359  }
360  }
361 
379  std::optional<PropertyValueViewToCopy<ElementType>>
380  get(double u, double v) const noexcept {
381  if (this->_status ==
383  return propertyValueViewToCopy(this->defaultValue());
384  }
385 
386  PropertyValueViewToCopy<ElementType> value = getRaw(u, v);
387 
388  if (value == this->noData()) {
389  return propertyValueViewToCopy(this->defaultValue());
390  } else if constexpr (IsMetadataNumeric<ElementType>::value) {
391  return transformValue(value, this->offset(), this->scale());
392  } else if constexpr (IsMetadataNumericArray<ElementType>::value) {
393  return transformArray(
394  propertyValueCopyToView(value),
395  this->offset(),
396  this->scale());
397  } else {
398  return value;
399  }
400  }
401 
417  getRaw(double u, double v) const noexcept {
418  CESIUM_ASSERT(
419  this->_status == PropertyTexturePropertyViewStatus::Valid &&
420  "Check the status() first to make sure view is valid");
421 
422  std::vector<uint8_t> sample =
423  this->sampleNearestPixel(u, v, this->_channels);
424 
425  return assembleValueFromChannels<ElementType>(
426  gsl::span(sample.data(), this->_channels.size()));
427  }
428 
432  const std::vector<int64_t>& getChannels() const noexcept {
433  return this->_channels;
434  }
435 
439  const std::string& getSwizzle() const noexcept { return this->_swizzle; }
440 
441 private:
442  std::vector<int64_t> _channels;
443  std::string _swizzle;
444 };
445 
446 #pragma endregion
447 
448 #pragma region Normalized property
449 
457 template <typename ElementType>
458 class PropertyTexturePropertyView<ElementType, true>
459  : public PropertyView<ElementType, true>, public TextureView {
460 private:
461  using NormalizedType = typename TypeToNormalizedType<ElementType>::type;
462 
463 public:
468  : PropertyView<ElementType, true>(),
469  TextureView(),
470  _channels(),
471  _swizzle() {}
472 
478  PropertyTexturePropertyView(PropertyViewStatusType status) noexcept
480  TextureView(),
481  _channels(),
482  _swizzle() {
483  CESIUM_ASSERT(
484  this->_status != PropertyTexturePropertyViewStatus::Valid &&
485  "An empty property view should not be constructed with a valid "
486  "status");
487  }
488 
497  PropertyTexturePropertyView(const ClassProperty& classProperty) noexcept
498  : PropertyView<ElementType, true>(classProperty),
499  TextureView(),
500  _channels(),
501  _swizzle() {
502  if (this->_status != PropertyTexturePropertyViewStatus::Valid) {
503  // Don't override the status / size if something is wrong with the class
504  // property's definition.
505  return;
506  }
507 
508  if (!classProperty.defaultProperty) {
509  // This constructor should only be called if the class property *has* a
510  // default value. But in the case that it does not, this property view
511  // becomes invalid.
512  this->_status =
514  return;
515  }
516 
518  }
519 
531  const PropertyTextureProperty& property,
532  const ClassProperty& classProperty,
533  const Sampler& sampler,
534  const ImageAsset& image,
535  const TextureViewOptions& options = TextureViewOptions()) noexcept
536  : PropertyView<ElementType, true>(classProperty, property),
537  TextureView(
538  sampler,
539  image,
540  property.texCoord,
541  property.getExtension<ExtensionKhrTextureTransform>(),
542  options),
543  _channels(property.channels),
544  _swizzle() {
545  if (this->_status != PropertyTexturePropertyViewStatus::Valid) {
546  return;
547  }
548 
549  switch (this->getTextureViewStatus()) {
551  break;
554  return;
557  return;
560  return;
562  this->_status =
564  return;
567  default:
569  return;
570  }
571 
572  _swizzle.reserve(_channels.size());
573  for (size_t i = 0; i < _channels.size(); ++i) {
574  switch (_channels[i]) {
575  case 0:
576  _swizzle += "r";
577  break;
578  case 1:
579  _swizzle += "g";
580  break;
581  case 2:
582  _swizzle += "b";
583  break;
584  case 3:
585  _swizzle += "a";
586  break;
587  default:
588  CESIUM_ASSERT(
589  false && "A valid channels vector must be passed to the view.");
590  }
591  }
592  }
593 
612  std::optional<PropertyValueViewToCopy<NormalizedType>>
613  get(double u, double v) const noexcept {
614  if (this->_status ==
616  return propertyValueViewToCopy(this->defaultValue());
617  }
618 
619  PropertyValueViewToCopy<ElementType> value = getRaw(u, v);
620 
621  if (value == this->noData()) {
622  return propertyValueViewToCopy(this->defaultValue());
623  } else if constexpr (IsMetadataScalar<ElementType>::value) {
624  return transformValue<NormalizedType>(
625  normalize<ElementType>(value),
626  this->offset(),
627  this->scale());
628  } else if constexpr (IsMetadataVecN<ElementType>::value) {
629  constexpr glm::length_t N = ElementType::length();
630  using T = typename ElementType::value_type;
631  using NormalizedT = typename NormalizedType::value_type;
632  return transformValue<glm::vec<N, NormalizedT>>(
633  normalize<N, T>(value),
634  this->offset(),
635  this->scale());
636  } else if constexpr (IsMetadataArray<ElementType>::value) {
637  using ArrayElementType = typename MetadataArrayType<ElementType>::type;
639  return transformNormalizedArray<ArrayElementType>(
640  propertyValueCopyToView(value),
641  this->offset(),
642  this->scale());
643  } else if constexpr (IsMetadataVecN<ArrayElementType>::value) {
644  constexpr glm::length_t N = ArrayElementType::length();
645  using T = typename ArrayElementType::value_type;
646  return transformNormalizedVecNArray<N, T>(
647  propertyValueCopyToView(value),
648  this->offset(),
649  this->scale());
650  }
651  }
652  }
653 
669  getRaw(double u, double v) const noexcept {
670  CESIUM_ASSERT(
671  this->_status == PropertyTexturePropertyViewStatus::Valid &&
672  "Check the status() first to make sure view is valid");
673 
674  std::vector<uint8_t> sample =
675  this->sampleNearestPixel(u, v, this->_channels);
676 
677  return assembleValueFromChannels<ElementType>(
678  gsl::span(sample.data(), this->_channels.size()));
679  }
680 
684  const std::vector<int64_t>& getChannels() const noexcept {
685  return this->_channels;
686  }
687 
691  const std::string& getSwizzle() const noexcept { return this->_swizzle; }
692 
693 private:
694  std::vector<int64_t> _channels;
695  std::string _swizzle;
696 };
697 #pragma endregion
698 
699 } // namespace CesiumGltf
Indicates the status of a property texture property view.
static const int ErrorUnsupportedProperty
This property view is associated with a ClassProperty of an unsupported type.
static const int ErrorChannelsAndTypeMismatch
The channels of this property texture property do not provide the exact number of bytes required by t...
static const int ErrorInvalidImage
This property view does not have a valid image index.
static const int ErrorInvalidChannels
The channels of this property texture property are invalid. Channels must be in the range 0-N,...
static const int ErrorInvalidBytesPerChannel
This property uses an image with multi-byte channels. Only single-byte channels are supported.
static const int ErrorInvalidSampler
This property view does not have a valid sampler index.
static const int ErrorInvalidPropertyTexture
This property view was initialized from an invalid PropertyTexture.
static const int ErrorInvalidTexture
This property view does not have a valid texture index.
static const int ErrorEmptyImage
This property is viewing an empty image.
const std::string & getSwizzle() const noexcept
Gets this property's channels as a swizzle string.
PropertyTexturePropertyView(PropertyViewStatusType status) noexcept
Constructs an invalid instance for an erroneous property.
PropertyTexturePropertyView(const ClassProperty &classProperty) noexcept
Constructs an instance of an empty property that specifies a default value. Although this property ha...
PropertyTexturePropertyView() noexcept
Constructs an invalid instance for a non-existent property.
PropertyValueViewToCopy< ElementType > getRaw(double u, double v) const noexcept
Gets the raw value of the property for the given texture coordinates. The sampler's wrapping mode wil...
const std::vector< int64_t > & getChannels() const noexcept
Gets the channels of this property texture property.
std::optional< PropertyValueViewToCopy< ElementType > > get(double u, double v) const noexcept
Gets the value of the property for the given texture coordinates with all value transforms applied....
PropertyTexturePropertyView(const PropertyTextureProperty &property, const ClassProperty &classProperty, const Sampler &sampler, const ImageAsset &image, const TextureViewOptions &options=TextureViewOptions()) noexcept
Construct a view of the data specified by a PropertyTextureProperty.
const std::string & getSwizzle() const noexcept
Gets this property's channels as a swizzle string.
PropertyTexturePropertyView(const ClassProperty &classProperty) noexcept
Constructs an instance of an empty property that specifies a default value. Although this property ha...
PropertyTexturePropertyView() noexcept
Constructs an invalid instance for a non-existent property.
PropertyValueViewToCopy< ElementType > getRaw(double u, double v) const noexcept
Gets the raw value of the property for the given texture coordinates. The sampler's wrapping mode wil...
std::optional< PropertyValueViewToCopy< NormalizedType > > get(double u, double v) const noexcept
Gets the value of the property for the given texture coordinates with all value transforms applied....
PropertyTexturePropertyView(PropertyViewStatusType status) noexcept
Constructs an invalid instance for an erroneous property.
PropertyTexturePropertyView(const PropertyTextureProperty &property, const ClassProperty &classProperty, const Sampler &sampler, const ImageAsset &image, const TextureViewOptions &options=TextureViewOptions()) noexcept
Construct a view of the data specified by a PropertyTextureProperty.
const std::vector< int64_t > & getChannels() const noexcept
Gets the channels of this property texture property.
A view of the data specified by a PropertyTextureProperty.
Indicates the status of a property view.
Definition: PropertyView.h:27
static const PropertyViewStatusType Valid
This property view is valid and ready to use.
Definition: PropertyView.h:32
static const PropertyViewStatusType ErrorNonexistentProperty
This property view is trying to view a property that does not exist.
Definition: PropertyView.h:47
static const PropertyViewStatusType EmptyPropertyWithDefault
This property view does not contain any data, but specifies a default value. This happens when a clas...
Definition: PropertyView.h:41
Represents a non-normalized metadata property in EXT_structural_metadata.
Definition: PropertyView.h:265
Represents a normalized metadata property in EXT_structural_metadata.
Definition: PropertyView.h:647
Represents a metadata property in EXT_structural_metadata.
Definition: PropertyView.h:251
Classes for working with glTF models.
@ ErrorEmptyImage
This texture is viewing an empty image.
@ ErrorUninitialized
This texture view has not yet been initialized.
@ Valid
This texture view is valid and ready to use.
@ ErrorInvalidSampler
This texture view does not have a valid sampler index.
@ ErrorInvalidImage
This texture view does not have a valid image index.
@ ErrorInvalidBytesPerChannel
The image for this texture has channels that take up more than a byte. Only single-byte channels are ...
@ ErrorInvalidTexture
This texture view does not have a valid texture index.
std::conditional_t< IsMetadataNumericArray< T >::value, PropertyArrayCopy< typename MetadataArrayType< T >::type >, T > PropertyValueViewToCopy
Transforms a property value type from a view to an equivalent type that owns the data it is viewing....
A class property.
Definition: ClassProperty.h:19
glTF extension that enables shifting and scaling UV coordinates on a per-texture basis
A 2D image asset, including its pixel data. The image may have mipmaps, and it may be encoded in a GP...
Definition: ImageAsset.h:34
Check if a C++ type can be represented as an array.
Check if a C++ type can be represented as an array of numeric elements property type.
Check if a C++ type can be represented as a numeric property, i.e. a scalar / vecN / matN type.
Check if a C++ type can be represented as a scalar property type.
Check if a C++ type can be represented as a vecN type.
A texture containing property values.
Texture sampler properties for filtering and wrapping modes.
Definition: Sampler.h:14
Describes options for constructing a view on a glTF texture.
Definition: TextureView.h:18
Convert an integer numeric type to the corresponding representation as a double type....