mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
561 lines
25 KiB
561 lines
25 KiB
#ifndef Magnum_TextureTools_Atlas_h |
|
#define Magnum_TextureTools_Atlas_h |
|
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
|
2020, 2021, 2022, 2023 Vladimír Vondruš <mosra@centrum.cz> |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a |
|
copy of this software and associated documentation files (the "Software"), |
|
to deal in the Software without restriction, including without limitation |
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
and/or sell copies of the Software, and to permit persons to whom the |
|
Software is furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included |
|
in all copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
DEALINGS IN THE SOFTWARE. |
|
*/ |
|
|
|
/** @file |
|
* @brief Class @ref Magnum::TextureTools::AtlasLandfill, enum @ref Magnum::TextureTools::AtlasLandfillFlag, enum set @ref Magnum::TextureTools::AtlasLandfillFlags, function @ref Magnum::TextureTools::atlas(), @ref Magnum::TextureTools::atlasArrayPowerOfTwo() |
|
*/ |
|
|
|
#include <Corrade/Containers/Pointer.h> |
|
|
|
#include "Magnum/Magnum.h" |
|
#include "Magnum/TextureTools/visibility.h" |
|
|
|
#ifdef MAGNUM_BUILD_DEPRECATED |
|
#include "Magnum/Math/Vector2.h" |
|
|
|
#include <Corrade/Utility/Macros.h> |
|
#include <Corrade/Utility/StlForwardVector.h> |
|
#endif |
|
|
|
namespace Magnum { namespace TextureTools { |
|
|
|
namespace Implementation { |
|
struct AtlasLandfillState; |
|
} |
|
|
|
/** |
|
@brief Landfill texture atlas packer behavior flag |
|
@m_since_latest |
|
|
|
@see @ref AtlasLandfillFlags, @ref AtlasLandfill::setFlags(), |
|
@ref AtlasLandfill::addFlags(), @ref AtlasLandfill::clearFlags() |
|
*/ |
|
enum class AtlasLandfillFlag { |
|
/** |
|
* Rotate all textures to a portrait orientation. Only one of |
|
* @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} can be set. If neither |
|
* is set, keeps the original orientation. |
|
*/ |
|
RotatePortrait = 1 << 0, |
|
|
|
/** |
|
* Rotate all textures to a landscape orientation. Only one of |
|
* @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} can be set. If neither |
|
* is set, keeps the original orientation. |
|
*/ |
|
RotateLandscape = 1 << 1, |
|
|
|
/** |
|
* Sort same-height textures widest first. Only one of |
|
* @ref AtlasLandfillFlag::WidestFirst and |
|
* @relativeref{AtlasLandfillFlag,NarrowestFirst} can be set. If neither is |
|
* set, textures of the same height keep their original order. |
|
*/ |
|
WidestFirst = 1 << 2, |
|
|
|
/** |
|
* Sort same-height textures narrowest first. Only one of |
|
* @ref AtlasLandfillFlag::WidestFirst and |
|
* @relativeref{AtlasLandfillFlag,NarrowestFirst} can be set. If neither is |
|
* set, textures of the same height keep their original order. |
|
*/ |
|
NarrowestFirst = 1 << 3, |
|
|
|
/** |
|
* By default, when reaching an edge, the next row is filled in reverse |
|
* direction only if the previous row ended lower than it started. If it |
|
* ended at the same height or higher, the next row is filled in the same |
|
* direction again in an attempt to level it out with decreasing heights. |
|
* Enabling this flag reverses the fill direction always. |
|
*/ |
|
ReverseDirectionAlways = 1 << 4 |
|
}; |
|
|
|
/** @debugoperatorenum{AtlasLandfillFlag} */ |
|
MAGNUM_TEXTURETOOLS_EXPORT Debug& operator<<(Debug& output, AtlasLandfillFlag value); |
|
|
|
/** |
|
@brief Landfill texture atlas packer behavior flags |
|
@m_since_latest |
|
|
|
@see @ref AtlasLandfill::setFlags(), @ref AtlasLandfill::addFlags(), |
|
@ref AtlasLandfill::clearFlags() |
|
*/ |
|
typedef Containers::EnumSet<AtlasLandfillFlag> AtlasLandfillFlags; |
|
|
|
CORRADE_ENUMSET_OPERATORS(AtlasLandfillFlags) |
|
|
|
/** @debugoperatorenum{AtlasLandfillFlags} */ |
|
MAGNUM_TEXTURETOOLS_EXPORT Debug& operator<<(Debug& output, AtlasLandfillFlags value); |
|
|
|
/** |
|
@brief Landfill texture atlas packer |
|
@m_since_latest |
|
|
|
Keeps track of currently filled height at every pixel with the aim to fill the |
|
available space bottom-up as evenly as possible. Packs to a 2D or a 2D array |
|
texture with either the height or depth optionally unbounded. See also |
|
@ref atlasArrayPowerOfTwo() for a variant that always provides optimal packing |
|
for power-of-two sizes. |
|
|
|
@htmlinclude atlas-landfill.svg |
|
|
|
* *The Trash Algorithm.* Naming credit goes to [\@lacyyy](https://github.com/lacyyy). |
|
|
|
@section TextureTools-AtlasLandfill-usage Example usage |
|
|
|
The following snippets shows packing a list of images into an atlas with the |
|
width set to 1024 and height unbounded. The algorithm by default makes all |
|
images the same orientation as that significantly improves the layout |
|
efficiency while not making any difference for texture mapping. |
|
|
|
@snippet TextureTools.cpp AtlasLandfill-usage |
|
|
|
Calculating a texture coordinate transformation matrix for a particular image |
|
can then be done with @ref atlasTextureCoordinateTransformation(), see its |
|
documentation for an example of how to calculate and apply the matrix to either |
|
the mesh directly or to a material / shader. |
|
|
|
If rotations are undesirable, for example if the resulting atlas is used by a |
|
linear rasterizer later, they can be disabled by clearing appropriate |
|
@ref AtlasLandfillFlags. The process can then also use the |
|
@ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector2i>&) |
|
overload without the rotations argument. |
|
|
|
@snippet TextureTools.cpp AtlasLandfill-usage-no-rotation |
|
|
|
@subsection TextureTools-AtlasLandfill-usage-atlas Array atlas |
|
|
|
The packing can be extended to a third dimension as well, in which case the |
|
packing overflows to next slices instead of expanding to potentially unbounded |
|
height. |
|
|
|
@snippet TextureTools.cpp AtlasLandfill-usage-array |
|
|
|
The layer has to be taken into an account in addition to the texture coordinate |
|
transformation matrix calculated with @ref atlasTextureCoordinateTransformation(), |
|
for example by adding a texture layer attribute to @ref Trade::MaterialData. |
|
|
|
@section TextureTools-AtlasLandfill-process Packing process |
|
|
|
On every @ref add(), the algorithm first makes all sizes the same orientation |
|
depending on @ref AtlasLandfillFlag::RotatePortrait or |
|
@relativeref{AtlasLandfillFlag,RotateLandscape} being set and sorts the sizes |
|
highest first and then depending on @ref AtlasLandfillFlag::WidestFirst or |
|
@relativeref{AtlasLandfillFlag,NarrowestFirst} being set. |
|
|
|
A per-pixel array of currently filled `heights`, initially all @cpp 0 @ce, and |
|
a horizontal insertion `cursor`, initially @cpp 0 @ce, is maintained. An item |
|
of given `size` gets placed at a `height` that's |
|
@cpp max(heights[cursor], heights[cursor + size.x]) @ce, this range gets then |
|
set to `height + size.y` and the cursor is updated to `cursor + size.x`. If |
|
cursor reaches the edge that an item cannot fit there anymore, it's reset to |
|
@cpp 0 @ce and the process continues again in the opposite direction, or the |
|
same direction if the previous row ended higher than it started. With the |
|
assumption that the texture sizes are uniformly distributed, this results in a |
|
fairly leveled out height. The process is aborted if the atlas height is |
|
bounded and the next item cannot fit there anymore. |
|
|
|
The sort is performed using @ref std::stable_sort(), which is usually |
|
@f$ \mathcal{O}(n \log{} n) @f$, the actual atlasing is a single |
|
@f$ \mathcal{O}(n) @f$ operation. Memory complexity is |
|
@f$ \mathcal{O}(n + wc) @f$ with @f$ n @f$ being a sorted copy of the input |
|
size array and @f$ wc @f$ being a 16-bit integer for every pixel of atlas width |
|
times filled atlas depth. Additionally @ref std::stable_sort() performs its own allocation. |
|
|
|
@section TextureTools-AtlasLandfill-incremental Incremental population |
|
|
|
It's possible to call @ref add() multiple times in order to incrementally fill |
|
the atlas with new data as much as the atlas height (if bounded) allows. In an |
|
ideal scenario, if the previous fill resulted in an uniform height the newly |
|
added data will be added in an optimal way as well, but in practice calling |
|
@ref add() with all data just once will always result in a more optimal |
|
packing than an incremental one. |
|
|
|
In case of an array atlas, the incremental process always starts from the first |
|
slice, finding the first that can fit the first (sorted) item. Then it attempts |
|
to place as many items as possible and on overflow continues searching for the |
|
next slice that can fit the first remaining item. If all slices are exhausted, |
|
adds a new one for as long as the depth (if bounded) allows. |
|
*/ |
|
class MAGNUM_TEXTURETOOLS_EXPORT AtlasLandfill { |
|
public: |
|
/** |
|
* @brief Constructor |
|
* |
|
* The @p size is expected to have non-zero width, and height not |
|
* larger than 65536. If height is @cpp 0 @ce, depth is expected to be |
|
* @cpp 1 @ce and the height is treated as unbounded, i.e. @ref add() |
|
* never fails. Otherwise, if depth is @cpp 0 @ce, depth is treated as |
|
* unbounded. |
|
*/ |
|
explicit AtlasLandfill(const Vector3i& size); |
|
|
|
/** |
|
* @brief Construct a non-array atlas |
|
* |
|
* Same as calling @ref AtlasLandfill with depth set to @cpp 1 @ce. |
|
*/ |
|
explicit AtlasLandfill(const Vector2i& size); |
|
|
|
/** @brief Copying is not allowed */ |
|
AtlasLandfill(const AtlasLandfill&) = delete; |
|
|
|
/** |
|
* @brief Move constructor |
|
* |
|
* Performs a destructive move, i.e. the original object isn't usable |
|
* afterwards anymore. |
|
*/ |
|
AtlasLandfill(AtlasLandfill&&) noexcept; |
|
|
|
~AtlasLandfill(); |
|
|
|
/** @brief Copying is not allowed */ |
|
AtlasLandfill& operator=(const AtlasLandfill&) = delete; |
|
|
|
/** @brief Move assignment */ |
|
AtlasLandfill& operator=(AtlasLandfill&&) noexcept; |
|
|
|
/** |
|
* @brief Atlas size specified in constructor |
|
* |
|
* @see @ref filledSize() |
|
*/ |
|
Vector3i size() const; |
|
|
|
/** |
|
* @brief Currently filled size |
|
* |
|
* Width is always taken from @ref size(). |
|
* |
|
* If @ref size() depth is @cpp 1 @ce, the returned depth is always |
|
* @cpp 1 @ce, height is @cpp 0 @ce initially, and at most the |
|
* height of @ref size() if it's bounded. It's calculated with a |
|
* @f$ \mathcal{O}(w) @f$ complexity, with @f$ w @f$ being the atlas |
|
* width. |
|
* |
|
* Otherwise, if @ref size() depth is not @cpp 1 @ce, the height is |
|
* taken from @ref size() and the depth is @cpp 0 @ce initially, and |
|
* at most @ref size() depth if the size is bounded. |
|
*/ |
|
Vector3i filledSize() const; |
|
|
|
/** |
|
* @brief Behavior flags |
|
* |
|
* Default is @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,WidestFirst}. |
|
*/ |
|
AtlasLandfillFlags flags() const; |
|
|
|
/** |
|
* @brief Set behavior flags |
|
* @return Reference to self (for method chaining) |
|
* |
|
* Note that some flags are mutually exclusive, see documentation of |
|
* particular @ref AtlasLandfillFlag values for more information. Can |
|
* be called with different values before each particular @ref add(). |
|
* @see @ref addFlags(), @ref clearFlags() |
|
*/ |
|
AtlasLandfill& setFlags(AtlasLandfillFlags flags); |
|
|
|
/** |
|
* @brief Add behavior flags |
|
* @return Reference to self (for method chaining) |
|
* |
|
* Calls @ref setFlags() with the existing flags ORed with @p flags. |
|
* Useful for preserving the defaults. |
|
* @see @ref clearFlags() |
|
*/ |
|
AtlasLandfill& addFlags(AtlasLandfillFlags flags) { |
|
return setFlags(this->flags()|flags); |
|
} |
|
|
|
/** |
|
* @brief Clear behavior flags |
|
* @return Reference to self (for method chaining) |
|
* |
|
* Calls @ref setFlags() with the existing flags ANDed with the inverse |
|
* of @p flags. Useful for preserving the defaults. |
|
* @see @ref addFlags() |
|
*/ |
|
AtlasLandfill& clearFlags(AtlasLandfillFlags flags) { |
|
return setFlags(this->flags() & ~flags); |
|
} |
|
|
|
/** |
|
* @brief Padding around each texture |
|
* |
|
* Default is a zero vector. |
|
*/ |
|
Vector2i padding() const; |
|
|
|
/** |
|
* @brief Set padding around each texture |
|
* @return Reference to self (for method chaining) |
|
* |
|
* Sizes are extended with twice the padding value before placement but |
|
* the returned offsets are without padding again. The third dimension |
|
* isn't treated in any special way. In order to have |
|
* @ref AtlasLandfillFlag::RotatePortrait and |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} work well also |
|
* with non-uniform padding, the padding is applied *before* a |
|
* potential rotation. I.e., the horizontal padding value is always |
|
* applied on input image width independently on how it's rotated |
|
* after. If you need different behavior, disable rotations with |
|
* @ref clearFlags() or pre-pad the input sizes directly instead of |
|
* using this function. |
|
* |
|
* Can be called with different values before each particular |
|
* @ref add(). |
|
*/ |
|
AtlasLandfill& setPadding(const Vector2i& padding); |
|
|
|
/** |
|
* @brief Add textures to the atlas |
|
* @param[in] sizes Texture sizes |
|
* @param[out] offsets Resulting offsets in the atlas |
|
* @param[out] rotations Which textures got rotated |
|
* |
|
* The @p sizes, @p offsets and @p rotations views are expected to have |
|
* the same size. The @p sizes are all expected to be not larger than |
|
* @ref size() after appying padding and then a rotation based on |
|
* @ref AtlasLandfillFlag::RotatePortrait or |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} being set. If |
|
* neither @relativeref{AtlasLandfillFlag,RotatePortrait} nor |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set, the |
|
* @p rotations view can be also empty or you can use the |
|
* @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector2i>&) |
|
* overload. The resulting @p offsets always point to the original |
|
* (potentially rotated) sizes without padding applied. |
|
* |
|
* Items with zero width or height don't contribute to the layout in |
|
* any way if padding is zero, but are still sorted, rotated and placed |
|
* relative to others. If padding is non-zero, items with zero width or |
|
* height are treated as any others to make sure they don't overlap |
|
* other items. |
|
* |
|
* On success returns @cpp true @ce and updates @ref filledSize(). If |
|
* @ref size() is bounded, can return @cpp false @ce if the items |
|
* didn't fit, in which case the internals and contents of @p offsets |
|
* and @p rotations are left in an undefined state. For an unbounded |
|
* @ref size() returns @cpp true @ce always. |
|
* @see @ref setFlags(), @ref setPadding() |
|
*/ |
|
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations); |
|
|
|
/** @overload */ |
|
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets, Containers::MutableBitArrayView rotations); |
|
|
|
/** |
|
* @brief Add textures to the atlas with rotations disabled |
|
* |
|
* Equivalent to calling @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&, Containers::MutableBitArrayView) |
|
* with the @p rotations view being empty. Can be called only if |
|
* neither @ref AtlasLandfillFlag::RotatePortrait nor |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set. |
|
* @see @ref clearFlags() |
|
*/ |
|
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); |
|
|
|
/** @overload */ |
|
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); |
|
|
|
/** |
|
* @brief Add textures to a non-array atlas |
|
* |
|
* Can be called only if @ref size() depth is @cpp 1 @ce. |
|
*/ |
|
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations); |
|
|
|
/** @overload */ |
|
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets, Containers::MutableBitArrayView rotations); |
|
|
|
/** |
|
* @brief Add textures to a non-array atlas with rotations disabled |
|
* |
|
* Equivalent to calling @ref add(const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector2i>&, Containers::MutableBitArrayView) |
|
* with the @p rotations view being empty. Can be called only if |
|
* @ref size() depth is @cpp 1 @ce and neither |
|
* @ref AtlasLandfillFlag::RotatePortrait nor |
|
* @relativeref{AtlasLandfillFlag,RotateLandscape} is set. |
|
* @see @ref clearFlags() |
|
*/ |
|
bool add(const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector2i>& offsets); |
|
|
|
/** @overload */ |
|
bool add(std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector2i>& offsets); |
|
|
|
private: |
|
Containers::Pointer<Implementation::AtlasLandfillState> _state; |
|
}; |
|
|
|
#ifdef MAGNUM_BUILD_DEPRECATED |
|
/** |
|
@brief Pack textures into a texture atlas |
|
@param atlasSize Size of the resulting atlas |
|
@param sizes Sizes of all textures in the atlas |
|
@param padding Padding around each texture |
|
|
|
Packs many small textures into one larger. If the textures cannot be packed |
|
into required size, an empty vector is returned. |
|
|
|
Padding is added twice to each size and the atlas is laid out so the padding |
|
don't overlap. Returned sizes are the same as original sizes, i.e. without the |
|
padding. |
|
|
|
@m_deprecated_since_latest Use the @ref AtlasLandfill class, which has a vastly |
|
better packing efficiency, supports incremental packing and doesn't force |
|
the caller to use a @ref std::vector. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT CORRADE_DEPRECATED("use the AtlasLandfill class instead") std::vector<Range2Di> atlas(const Vector2i& atlasSize, const std::vector<Vector2i>& sizes, const Vector2i& padding = {}); |
|
#endif |
|
|
|
/** |
|
@brief Pack square power-of-two textures into a texture atlas array |
|
@param[in] layerSize Size of a single layer in the texture atlas |
|
@param[in] sizes Sizes of all textures in the atlas |
|
@param[out] offsets Resulting offsets in the atlas |
|
@return Total layer count |
|
@m_since_latest |
|
|
|
The @p sizes and @p offsets views are expected to have the same size. The |
|
@p layerSize is expected to be non-zero, square and power-of-two. All items in |
|
@p sizes are expected to be non-zero, square, power-of-two and not larger than |
|
@p layerSize. With such constraints the packing is optimal with no wasted space |
|
in all but the last layer. Setting @p layerSize to the size of the largest |
|
texture in the set will lead to the least wasted space in the last layer. |
|
|
|
@htmlinclude atlas-array-power-of-two.svg |
|
|
|
Example usage is shown below. Calculating a texture coordinate transformation |
|
matrix for a particular image can then be done with |
|
@ref atlasTextureCoordinateTransformation(), see its documentation for how to |
|
calculate and apply the matrix to either the mesh directly or to a material / |
|
shader. |
|
|
|
@snippet TextureTools.cpp atlasArrayPowerOfTwo |
|
|
|
The algorithm first sorts the textures by size using @ref std::stable_sort(), |
|
which is usually @f$ \mathcal{O}(n \log{} n) @f$, and then performs the actual |
|
atlasing in a single @f$ \mathcal{O}(n) @f$ operation. Memory complexity is |
|
@f$ \mathcal{O}(n) @f$ with @f$ n @f$ being a sorted copy of the input size |
|
array, additionally @ref std::stable_sort() performs its own allocation. See |
|
the [Zero-waste single-pass packing of power-of-two textures](https://blog.magnum.graphics/backstage/pot-array-packing/) |
|
article for a detailed description of the algorithm. |
|
|
|
See the @ref AtlasLandfill class for an alternative that isn't restricted to |
|
power-of-two sizes and can be used in an incremental way but doesn't always |
|
produce optimal packing. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT Int atlasArrayPowerOfTwo(const Vector2i& layerSize, const Containers::StridedArrayView1D<const Vector2i>& sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); |
|
|
|
/** |
|
* @overload |
|
* @m_since_latest |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT Int atlasArrayPowerOfTwo(const Vector2i& layerSize, std::initializer_list<Vector2i> sizes, const Containers::StridedArrayView1D<Vector3i>& offsets); |
|
|
|
#ifdef MAGNUM_BUILD_DEPRECATED |
|
/** |
|
@brief @copybrief atlasArrayPowerOfTwo(const Vector2i&, const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&) |
|
@m_deprecated_since_latest Use @ref atlasArrayPowerOfTwo(const Vector2i&, const Containers::StridedArrayView1D<const Vector2i>&, const Containers::StridedArrayView1D<Vector3i>&) |
|
instead. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT CORRADE_DEPRECATED("use the overload taking offsets as an output view instead") Containers::Pair<Int, Containers::Array<Vector3i>> atlasArrayPowerOfTwo(const Vector2i& layerSize, const Containers::StridedArrayView1D<const Vector2i>& sizes); |
|
|
|
/** |
|
@overload |
|
@m_deprecated_since_latest Use @ref atlasArrayPowerOfTwo(const Vector2i&, std::initializer_list<Vector2i>, const Containers::StridedArrayView1D<Vector3i>&) |
|
instead. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT CORRADE_DEPRECATED("use the overload taking offsets as an output view instead") Containers::Pair<Int, Containers::Array<Vector3i>> atlasArrayPowerOfTwo(const Vector2i& layerSize, std::initializer_list<Vector2i> sizes); |
|
#endif |
|
|
|
/** |
|
@brief Calculate a texture coordinate transformation matrix for an atlas-packed item |
|
@m_since_latest |
|
|
|
Together with @ref atlasTextureCoordinateTransformationRotatedCounterClockwise() |
|
or @ref atlasTextureCoordinateTransformationRotatedClockwise() meant be used to |
|
adjust mesh texture coordinate attributes after packing textures with |
|
@ref AtlasLandfill or @ref atlasArrayPowerOfTwo(). Expects that @p size and |
|
@p offset fit into the @p atlasSize, the rotated variants expect that @p size |
|
with coordinates flipped and @p offset fit into the @p atlasSize. |
|
|
|
With a concrete `atlasSize`, `sizes` being the input sizes passed to |
|
@ref AtlasLandfill::add() (i.e., without any potential rotations applied yet), |
|
and `offsets` and `rotations` being the output, the usage is as follows: |
|
|
|
@snippet TextureTools.cpp atlasTextureCoordinateTransformation |
|
|
|
The resulting matrix can be then directly used to adjust texture coordinates, |
|
like below with @ref MeshTools::transformTextureCoordinates2DInPlace() on a |
|
@link Trade::MeshData @endlink: |
|
|
|
@snippet TextureTools.cpp atlasTextureCoordinateTransformation-meshdata |
|
|
|
Alternatively, for example in cases where a single mesh is used with several different textures, the transformation can be applied at draw time, such as |
|
with @ref Shaders::FlatGL::setTextureMatrix(). In case there's already a |
|
texture transformation matrix being applied when drawing, the new |
|
transformation has to happen *after*, so multiplied from the left side. For |
|
example with a @ref Trade::MaterialData that contains a |
|
@link Trade::MaterialAttribute::TextureMatrix @endlink: |
|
|
|
@snippet TextureTools.cpp atlasTextureCoordinateTransformation-materialdata |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT Matrix3 atlasTextureCoordinateTransformation(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset); |
|
|
|
/** |
|
@brief Calculate a texture coordinate transformation matrix for an atlas-packed item rotated counterclockwise |
|
@m_since_latest |
|
|
|
Like @ref atlasTextureCoordinateTransformation(), but swaps X and Y of @p size |
|
and produces a matrix that rotates the texture coordinates 90° |
|
counterclockwise. The lower left corner of the input becomes a lower right |
|
corner. See @ref atlasTextureCoordinateTransformationRotatedClockwise() for a |
|
clockwise variant. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT Matrix3 atlasTextureCoordinateTransformationRotatedCounterClockwise(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset); |
|
|
|
/** |
|
@brief Calculate a texture coordinate transformation matrix for an atlas-packed item rotated clockwise |
|
@m_since_latest |
|
|
|
Like @ref atlasTextureCoordinateTransformation(), but swaps X and Y of @p size |
|
and produces a matrix that rotates the texture coordinates 90° clockwise. The lower left corner of the input becomes an upper left corner. See |
|
@ref atlasTextureCoordinateTransformationRotatedClockwise() for a |
|
counterclockwise variant. |
|
*/ |
|
MAGNUM_TEXTURETOOLS_EXPORT Matrix3 atlasTextureCoordinateTransformationRotatedClockwise(const Vector2i& atlasSize, const Vector2i& size, const Vector2i& offset); |
|
|
|
}} |
|
|
|
#endif
|
|
|