You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
8.6 KiB
C++
156 lines
8.6 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
#pragma once
|
|
|
|
#include <AzCore/Math/Aabb.h>
|
|
#include <AzCore/Math/Vector3.h>
|
|
#include <AzCore/Math/Matrix3x4.h>
|
|
#include <AzCore/Math/Transform.h>
|
|
#include <AzCore/std/functional.h>
|
|
|
|
namespace GradientSignal
|
|
{
|
|
//! Controls how the gradient repeats itself when queried outside the bounds of the shape.
|
|
enum class WrappingType : AZ::u8
|
|
{
|
|
None = 0, //! Unbounded - the gradient ignores the shape bounds.
|
|
ClampToEdge, //! The values on the edge of the shape will be extended outward in each direction.
|
|
Mirror, //! The gradient signal will be repeated but mirrored on every repeat.
|
|
Repeat, //! The gradient signal will be repeated in every direction.
|
|
ClampToZero, //! The value will always be 0 outside of the shape.
|
|
};
|
|
|
|
class GradientTransform
|
|
{
|
|
public:
|
|
GradientTransform() = default;
|
|
|
|
/**
|
|
* Create a GradientTransform with the given parameters.
|
|
* GradientTransform is a utility class that converts world space positions to gradient space UVW values which can be used
|
|
* to look up deterministic gradient values for the input spatial locations.
|
|
* \param shapeBounds The bounds of the shape associated with the gradient, in local space.
|
|
* \param transform The transform to use to convert from world space to gradient space.
|
|
* \param use3d True for 3D gradient lookup outputs, false for 2D gradient lookup outputs. (i.e. output W will be nonzero or zero)
|
|
* \param frequencyZoom Amount to scale the UVW results after wrapping is applied.
|
|
* \param wrappingType The way in which the gradient repeats itself outside the shape bounds.
|
|
*/
|
|
GradientTransform(
|
|
const AZ::Aabb& shapeBounds,
|
|
const AZ::Matrix3x4& transform,
|
|
bool use3d,
|
|
float frequencyZoom,
|
|
GradientSignal::WrappingType wrappingType);
|
|
|
|
/**
|
|
* Checks to see if two GradientTransform instances are equivalent.
|
|
* Useful for being able to send out notifications when a GradientTransform has changed.
|
|
* \param rhs The second GradientTranform to compare against.
|
|
* \return True if they're equal, False if they aren't.
|
|
*/
|
|
bool operator==(const GradientTransform& rhs) const
|
|
{
|
|
return (
|
|
(m_shapeBounds == rhs.m_shapeBounds) &&
|
|
(m_inverseTransform == rhs.m_inverseTransform) &&
|
|
(m_alwaysAcceptPoint == rhs.m_alwaysAcceptPoint) &&
|
|
(m_frequencyZoom == rhs.m_frequencyZoom) &&
|
|
(m_wrappingType == rhs.m_wrappingType) &&
|
|
(m_normalizeExtentsReciprocal == rhs.m_normalizeExtentsReciprocal));
|
|
}
|
|
|
|
/**
|
|
* Checks to see if two GradientTransform instances aren't equivalent.
|
|
* Useful for being able to send out notifications when a GradientTransform has changed.
|
|
* \param rhs The second GradientTranform to compare against.
|
|
* \return True if they're not equal, False if they are.
|
|
*/
|
|
bool operator!=(const GradientTransform& rhs) const
|
|
{
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
|
|
/**
|
|
* Transform the given world space position to a gradient space UVW lookup value.
|
|
* \param inPosition The input world space position to transform.
|
|
* \param outUVW [out] The UVW value that can be used to look up a deterministic gradient value.
|
|
* \param wasPointRejected [out] True if the input position doesn't have a gradient value, false if it does.
|
|
* Most gradients have values mapped to infinite world space, so wasPointRejected will almost always be false.
|
|
* It will only be true when using ClampToZero and the world space position falls outside the shape bounds.
|
|
*/
|
|
void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const;
|
|
|
|
/**
|
|
* Transform the given world space position to a gradient space UVW lookup value and normalize to the shape bounds.
|
|
* "Normalizing" in this context means that regardless of the world space coordinates, (0,0,0) represents the minimum
|
|
* shape bounds corner, and (1,1,1) represents the maximum shape bounds corner. Depending on the wrapping type, it's possible
|
|
* (and even likely) to get values outside the 0-1 range.
|
|
* \param inPosition The input world space position to transform.
|
|
* \param outUVW [out] The UVW value that can be used to look up a deterministic gradient value.
|
|
* \param wasPointRejected [out] True if the input position doesn't have a gradient value, false if it does.
|
|
* Most gradients have values mapped to infinite world space, so wasPointRejected will almost always be false.
|
|
* It will only be true when using ClampToZero and the world space position falls outside the shape bounds.
|
|
*/
|
|
void TransformPositionToUVWNormalized(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const;
|
|
|
|
/**
|
|
* Epsilon value to allow our UVW range to go to [min, max) by using the range [min, max - epsilon].
|
|
* To keep things behaving consistently between clamped and unbounded uv ranges, we want our clamped uvs to use a
|
|
* range of [min, max), so we'll actually clamp to [min, max - epsilon]. Since our floating-point numbers are likely in the
|
|
* -16384 to 16384 range, an epsilon of 0.001 will work without rounding to 0.
|
|
* (This constant is public so that it can be used from unit tests for validating transformation results)
|
|
*/
|
|
static constexpr float UvEpsilon = 0.001f;
|
|
|
|
private:
|
|
|
|
//! These are the various transformations that will be performed, based on wrapping type.
|
|
static AZ::Vector3 NoTransform(const AZ::Vector3& point, const AZ::Aabb& bounds);
|
|
static AZ::Vector3 GetUnboundedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
|
|
static AZ::Vector3 GetClampedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
|
|
static AZ::Vector3 GetMirroredPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
|
|
static AZ::Vector3 GetRelativePointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
|
|
static AZ::Vector3 GetWrappedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
|
|
|
|
//! The shape bounds are used for determining the wrapping bounds, and to normalize the UVW results into if requested.
|
|
AZ::Aabb m_shapeBounds = AZ::Aabb::CreateNull();
|
|
|
|
/**
|
|
* The relative transform to use for converting from world space to gradient space, stored as an inverse transform.
|
|
* We only ever need to use the inverse transform, so we compute it once and store it instead of keeping the original
|
|
* transform around. Note that the GradientTransformComponent has many options for choosing which relative space to use
|
|
* for the transform, so the transform passed in to this class might already have many modifications applied to it.
|
|
* The inverse transform will also get its 3rd row cleared out if "use3d" is false and we're only performing 2D gradient
|
|
* transformations, so that the W component of the UVW output will always be 0.
|
|
*/
|
|
AZ::Matrix3x4 m_inverseTransform = AZ::Matrix3x4::CreateIdentity();
|
|
|
|
/**
|
|
* Whether or not to always accept the input point as a valid output point.
|
|
* Most of the time, the gradient exists everywhere in world space, so we always accept the input point.
|
|
* The one exception is ClampToZero, which will return that the point is rejected if it falls outside the shape bounds.
|
|
*/
|
|
bool m_alwaysAcceptPoint = true;
|
|
|
|
//! Apply a scale to the point *after* the wrapping is applied.
|
|
float m_frequencyZoom = 1.0f;
|
|
|
|
//! How the gradient should repeat itself outside of the shape bounds.
|
|
WrappingType m_wrappingType = WrappingType::None;
|
|
|
|
/**
|
|
* Cached reciprocal for performing an inverse lerp back to shape bounds.
|
|
* When normalizing the output UVW back into the shape bounds, we perform an inverse lerp. The inverse lerp
|
|
* equation is (point - min) * (1 / (max-min)), so we save off the (1 / (max-min)) term to avoid recalculating it on every point.
|
|
*/
|
|
AZ::Vector3 m_normalizeExtentsReciprocal = AZ::Vector3(1.0f);
|
|
};
|
|
|
|
} // namespace GradientSignal
|