Skip to content

Commit

Permalink
Most of a texture property array implementation pending PropertyArray…
Browse files Browse the repository at this point in the history
…Copy modifications
  • Loading branch information
azrogers committed Jan 31, 2025
1 parent 9bd16a4 commit 9b8fb66
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 44 deletions.
251 changes: 247 additions & 4 deletions CesiumGltf/include/CesiumGltf/PropertyTexturePropertyView.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,28 @@ int64_t assembleEnumValue(
const std::span<uint8_t> bytes,
PropertyComponentType componentType) noexcept;

/**
* @brief Attempts to obtain an `int64_t` array value from the given span of bytes.
*
* @tparam T The element type to read from `bytes`.
* @param bytes A span of bytes to convert into an array value.
* @returns A \ref PropertyArrayCopy containing the elements read.
*/
PropertyArrayCopy<PropertyEnumValue>
assembleEnumArrayValue(const std::span<uint8_t> bytes, PropertyComponentType componentType) noexcept {
const size_t componentSize = getSizeOfComponentType(componentType);
const size_t numEnums = bytes.size() / componentSize;
std::vector<PropertyEnumValue> result;
result.reserve(numEnums);

for(size_t i = 0; i < result.size(); i++) {
const int64_t val = assembleEnumValue(bytes.subspan(i * componentSize), componentType);
result.emplace_back(val);
}

return PropertyArrayCopy<PropertyEnumValue>(result);
}

/**
* @brief Attempts to obtain a vector value from the given span of bytes.
*
Expand Down Expand Up @@ -650,17 +672,17 @@ class PropertyTexturePropertyView<PropertyEnumValue>
* @return The value of the element, or std::nullopt if it matches the "no
* data" value
*/
std::optional<PropertyValueViewToCopy<PropertyEnumValue>>
std::optional<PropertyEnumValue>
get(double u, double v) const noexcept {
if (this->_status ==
PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault) {
return propertyValueViewToCopy(this->defaultValue());
return this->defaultValue();
}

PropertyValueViewToCopy<PropertyEnumValue> value = getRaw(u, v);
PropertyEnumValue value = getRaw(u, v);

if (value == this->noData()) {
return propertyValueViewToCopy(this->defaultValue());
return this->defaultValue();
} else {
return value;
}
Expand Down Expand Up @@ -714,6 +736,227 @@ class PropertyTexturePropertyView<PropertyEnumValue>
PropertyComponentType _componentType;
};

/**
* @brief A view of enum data specified by a
* {@link PropertyTextureProperty}.
*
* Provides utilities to sample the property texture property using texture
* coordinates.
*
* @tparam ElementType The type of the elements represented in the property view
*/
template <>
class PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>
: public PropertyView<PropertyArrayView<PropertyEnumValue>>, public TextureView {
public:
/**
* @brief Constructs an invalid instance for a non-existent property.
*/
PropertyTexturePropertyView() noexcept
: PropertyView<PropertyArrayView<PropertyEnumValue>>(),
TextureView(),
_channels(),
_swizzle() {}

/**
* @brief Constructs an invalid instance for an erroneous property.
*
* @param status The code from {@link PropertyTexturePropertyViewStatus} indicating the error with the property.
*/
PropertyTexturePropertyView(PropertyViewStatusType status) noexcept
: PropertyView<PropertyArrayView<PropertyEnumValue>>(status),
TextureView(),
_channels(),
_swizzle() {
CESIUM_ASSERT(
this->_status != PropertyTexturePropertyViewStatus::Valid &&
"An empty property view should not be constructed with a valid status");
}

/**
* @brief Constructs an instance of an empty property that specifies a default
* value. Although this property has no data, it can return the default value
* when \ref get is called. However, \ref getRaw cannot be used.
*
* @param classProperty The {@link ClassProperty} this property conforms to.
*/
PropertyTexturePropertyView(const ClassProperty& classProperty) noexcept
: PropertyView<PropertyArrayView<PropertyEnumValue>>(classProperty),
TextureView(),
_channels(),
_swizzle() {
if (this->_status != PropertyTexturePropertyViewStatus::Valid) {
// Don't override the status / size if something is wrong with the class
// property's definition.
return;
}

if (!classProperty.defaultProperty) {
// This constructor should only be called if the class property *has* a
// default value. But in the case that it does not, this property view
// becomes invalid.
this->_status =
PropertyTexturePropertyViewStatus::ErrorNonexistentProperty;
return;
}

this->_status = PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault;
}

/**
* @brief Construct a view of the data specified by a {@link PropertyTextureProperty}.
*
* @param property The {@link PropertyTextureProperty}
* @param classProperty The {@link ClassProperty} this property conforms to.
* @param pEnumDefinition The {@link CesiumGltf::Enum} defining the values for this property.
* @param sampler The {@link Sampler} used by the property.
* @param image The {@link ImageAsset} used by the property.
* @param options The options for constructing the view.
*/
PropertyTexturePropertyView(
const PropertyTextureProperty& property,
const ClassProperty& classProperty,
const CesiumGltf::Enum* pEnumDefinition,
const Sampler& sampler,
const ImageAsset& image,
const TextureViewOptions& options = TextureViewOptions()) noexcept
: PropertyView<PropertyArrayView<PropertyEnumValue>>(classProperty, pEnumDefinition),
TextureView(
sampler,
image,
property.texCoord,
property.getExtension<ExtensionKhrTextureTransform>(),
options),
_channels(property.channels),
_swizzle(),
_componentType(
convertStringToPropertyComponentType(pEnumDefinition->valueType)) {
if (this->_status != PropertyTexturePropertyViewStatus::Valid) {
return;
}

switch (this->getTextureViewStatus()) {
case TextureViewStatus::Valid:
break;
case TextureViewStatus::ErrorInvalidSampler:
this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidSampler;
return;
case TextureViewStatus::ErrorInvalidImage:
this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidImage;
return;
case TextureViewStatus::ErrorEmptyImage:
this->_status = PropertyTexturePropertyViewStatus::ErrorEmptyImage;
return;
case TextureViewStatus::ErrorInvalidBytesPerChannel:
this->_status =
PropertyTexturePropertyViewStatus::ErrorInvalidBytesPerChannel;
return;
case TextureViewStatus::ErrorUninitialized:
case TextureViewStatus::ErrorInvalidTexture:
default:
this->_status = PropertyTexturePropertyViewStatus::ErrorInvalidTexture;
return;
}

_swizzle.reserve(_channels.size());

for (size_t i = 0; i < _channels.size(); ++i) {
switch (_channels[i]) {
case 0:
_swizzle += "r";
break;
case 1:
_swizzle += "g";
break;
case 2:
_swizzle += "b";
break;
case 3:
_swizzle += "a";
break;
default:
CESIUM_ASSERT(
false && "A valid channels vector must be passed to the view.");
}
}
}

/**
* @brief Gets the value of the property for the given texture coordinates.
* The sampler's wrapping mode will be used when sampling the texture.
*
* Value transforms are not applied for enum values, so this method is
* equivalent to @ref getRaw, except that if this property has a specified "no
* data" value, this will return the property's default value for any elements
* that equal this "no data" value. If the property did not specify a default
* value, this returns std::nullopt.
*
* @param u The u-component of the texture coordinates.
* @param v The v-component of the texture coordinates.
*
* @return The value of the element, or std::nullopt if it matches the "no
* data" value
*/
std::optional<PropertyArrayCopy<PropertyEnumValue>>
get(double u, double v) const noexcept {
if (this->_status ==
PropertyTexturePropertyViewStatus::EmptyPropertyWithDefault) {
return propertyValueViewToCopy(this->defaultValue());
}

PropertyArrayCopy<PropertyEnumValue> value = getRaw(u, v);

if (value == this->noData()) {
return propertyValueViewToCopy(this->defaultValue());
} else {
return value;
}
}

/**
* @brief Gets the raw value of the property for the given texture
* coordinates. The sampler's wrapping mode will be used when sampling the
* texture.
*
* If this property has a specified "no data" value, the raw value will still
* be returned, even if it equals the "no data" value.
*
* @param u The u-component of the texture coordinates.
* @param v The v-component of the texture coordinates.
*
* @return The value at the nearest pixel to the texture coordinates.
*/

PropertyArrayCopy<PropertyEnumValue>
getRaw(double u, double v) const noexcept {
CESIUM_ASSERT(
this->_status == PropertyTexturePropertyViewStatus::Valid &&
"Check the status() first to make sure view is valid");

std::vector<uint8_t> sample =
this->sampleNearestPixel(u, v, this->_channels);

return assembleEnumArrayValue(sample, _componentType);
}

/**
* @brief Gets the channels of this property texture property.
*/
const std::vector<int64_t>& getChannels() const noexcept {
return this->_channels;
}

/**
* @brief Gets this property's channels as a swizzle string.
*/
const std::string& getSwizzle() const noexcept { return this->_swizzle; }

private:
std::vector<int64_t> _channels;
std::string _swizzle;
PropertyComponentType _componentType;
};

#pragma endregion

#pragma region Normalized property
Expand Down
100 changes: 100 additions & 0 deletions CesiumGltf/include/CesiumGltf/PropertyTextureView.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ class PropertyTextureView {
propertyOptions);
}

if constexpr (IsMetadataEnumArray<T>::value) {
return createEnumArrayPropertyView(classProperty, propertyTextureProperty, propertyOptions);
}

if constexpr (IsMetadataArray<T>::value) {
return createArrayPropertyView<
typename MetadataArrayType<T>::type,
Expand Down Expand Up @@ -716,6 +720,102 @@ class PropertyTextureView {
propertyOptions);
}

PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>
createEnumArrayPropertyView(
const ClassProperty& classProperty,
const PropertyTextureProperty& propertyTextureProperty,
[[maybe_unused]] const TextureViewOptions& propertyOptions) const {
if (!classProperty.array) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(
PropertyTexturePropertyViewStatus::ErrorArrayTypeMismatch);
}

const PropertyType type = convertStringToPropertyType(classProperty.type);
if (TypeToPropertyType<PropertyEnumValue>::value != type) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(
PropertyTexturePropertyViewStatus::ErrorTypeMismatch);
}

if (!classProperty.enumType) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(
PropertyTexturePropertyViewStatus::ErrorInvalidEnumType);
}

const auto& enumDefinitionIt =
this->_pEnumDefinitions->find(*classProperty.enumType);
if (enumDefinitionIt == this->_pEnumDefinitions->end()) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(
PropertyTexturePropertyViewStatus::ErrorInvalidEnumType);
}

const Enum* pEnumDefinition = &enumDefinitionIt->second;

const PropertyComponentType componentType =
convertStringToPropertyComponentType(pEnumDefinition->valueType);
const size_t componentSize = getSizeOfComponentType(componentType);

// We can only grab up to four bytes from the texture.
if (componentSize > 4) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(
PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty);
}

const int64_t count = classProperty.count.value_or(0);
if(count <= 0 || count > 4) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(
PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty);
}

const size_t arraySize = static_cast<size_t>(count) * componentSize;

if(arraySize > 4) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(
PropertyTexturePropertyViewStatus::ErrorUnsupportedProperty);
}

int32_t samplerIndex;
int32_t imageIndex;

PropertyViewStatusType status =
getTextureSafe(propertyTextureProperty.index, samplerIndex, imageIndex);

if (status != PropertyTexturePropertyViewStatus::Valid) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(status);
}

status = checkSampler(samplerIndex);
if (status != PropertyTexturePropertyViewStatus::Valid) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(status);
}

status = checkImage(imageIndex);
if (status != PropertyTexturePropertyViewStatus::Valid) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(status);
}

const CesiumUtility::IntrusivePointer<ImageAsset>& pImage =
_pModel->images[static_cast<size_t>(imageIndex)].pAsset;
const std::vector<int64_t>& channels = propertyTextureProperty.channels;

status = checkChannels(channels, *pImage);
if (status != PropertyTexturePropertyViewStatus::Valid) {
return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(status);
}

if (channels.size() * static_cast<size_t>(pImage->bytesPerChannel) !=
arraySize) {
return PropertyTexturePropertyViewStatus::ErrorChannelsAndTypeMismatch;
}

return PropertyTexturePropertyView<PropertyArrayView<PropertyEnumValue>>(
propertyTextureProperty,
classProperty,
pEnumDefinition,
_pModel->samplers[samplerIndex],
*pImage,
propertyOptions);
}

template <typename T, bool Normalized>
PropertyTexturePropertyView<T, Normalized> createVecNPropertyView(
const ClassProperty& classProperty,
Expand Down
Loading

0 comments on commit 9b8fb66

Please sign in to comment.