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.
o3de/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.h

222 lines
8.8 KiB
C++

/*
* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
*or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <qimage.h>
#include <TextureAtlas/TextureAtlasBus.h>
namespace TextureAtlasBuilder
{
//! Struct that is used to communicate input commands
struct AtlasBuilderInput
{
AZ_CLASS_ALLOCATOR(AtlasBuilderInput, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(AtlasBuilderInput, "{F54477F9-1BDE-4274-8CC0-8320A3EF4A42}");
bool m_forceSquare;
bool m_forcePowerOf2;
// Includes a white default texture for the UI to use under certain circumstances
bool m_includeWhiteTexture;
int m_maxDimension;
// At least this much padding will surround each texture except on the edges of the atlas
int m_padding;
// Color used in wasted space
AZ::Color m_unusedColor;
// A preset to use for the texture atlas image processing
AZStd::string m_presetName;
AZStd::vector<AZStd::string> m_filePaths;
AtlasBuilderInput():
m_forceSquare(false),
m_forcePowerOf2(false),
m_includeWhiteTexture(true),
m_maxDimension(4096),
m_padding(1),
// Default color should be a non-transparent color that isn't used often in uis
m_unusedColor(.235f, .702f, .443f, 1)
{
}
static void Reflect(AZ::ReflectContext* context);
//! Attempts to read the input from a .texatlas file. "valid" is for reporting exceptions and telling the asset
//! proccesor to fail the job. Supports parsing through a human readable custom parser.
static AtlasBuilderInput ReadFromFile(const AZStd::string& path, const AZStd::string& directory, bool& valid);
//! Resolves any wild cards in paths
static void AddFilesUsingWildCard(AZStd::vector<AZStd::string>& paths, const AZStd::string& insert);
//! Removes anything that matches the wildcard
static void RemoveFilesUsingWildCard(AZStd::vector<AZStd::string>& paths, const AZStd::string& remove);
//! Resolves any folder paths into image file paths
static void AddFolderContents(AZStd::vector<AZStd::string>& paths, const AZStd::string& insert, bool& valid);
//! Resolves remove commands for folders
static void RemoveFolderContents(AZStd::vector<AZStd::string>& paths, const AZStd::string& remove);
};
//! Struct that is used to represent an object with a width and height in pixels
struct ImageDimension
{
int m_width;
int m_height;
ImageDimension(int width, int height)
{
m_width = width;
m_height = height;
}
};
//! Typedef for an ImageDimension paired with an integer
using IndexImageDimension = AZStd::pair<int, ImageDimension>;
//! Typedef for a list of ImageDimensions paired with integers
using ImageDimensionData = AZStd::vector<IndexImageDimension>;
//! Typedef to simplify references to TextureAtlas::AtlasCoordinates
using AtlasCoordinates = TextureAtlasNamespace::AtlasCoordinates;
//! Number of bytes in a pixel
const int bytesPerPixel = 4;
//! The size of the padded sorting units (important for compression)
const int cellSize = 4;
//! Indexes of the products
enum class Product
{
TexatlasidxProduct = 0,
DdsProduct = 1
};
//! An asset builder for texture atlases
class AtlasBuilderWorker : public AssetBuilderSDK::AssetBuilderCommandBus::Handler
{
public:
AZ_RTTI(AtlasBuilderWorker, "{79036188-E017-4575-9EC0-8D39CB560EA6}");
AtlasBuilderWorker() = default;
~AtlasBuilderWorker() = default;
//! Asset Builder Callback Functions
//! Called by asset processor to gather information on a job for a ".texatlas" file
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request,
AssetBuilderSDK::CreateJobsResponse& response);
//! Called by asset proccessor when it wants us to execute a job
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request,
AssetBuilderSDK::ProcessJobResponse& response);
//! Returns the job related information used by the builder
static AssetBuilderSDK::JobDescriptor GetJobDescriptor(const AZStd::string& sourceFile, const AtlasBuilderInput& input);
//////////////////////////////////////////////////////////////////////////
//! AssetBuilderSDK::AssetBuilderCommandBus interface
void ShutDown() override; // if you get this you must fail all existing jobs and return.
//////////////////////////////////////////////////////////////////////////
private:
bool m_isShuttingDown = false;
//! This is the main function that takes a set of inputs and attempts to pack them into an atlas of a given
//! size. Returns true if succesful, does not update out on failure.
static bool TryPack(const ImageDimensionData& images,
int targetWidth,
int targetHeight,
int padding,
size_t& amountFit,
AZStd::vector<AtlasCoordinates>& out);
//! Removes any overlap between slotList and the given item
static void TrimOverlap(AZStd::vector<AtlasCoordinates>& slotList, AtlasCoordinates item);
//! Uses the proper tightening method based on the input and returns the maximum number of items that were able to be fit
bool TryTightening(AtlasBuilderInput input,
const ImageDimensionData& images,
int smallestWidth,
int smallestHeight,
int targetArea,
int padding,
int& resultWidth,
int& resultHeight,
size_t& amountFit,
AZStd::vector<AtlasCoordinates>& out);
//! Finds the tightest square fit achievable by expanding a square area until a valid fit is found
bool TryTighteningSquare(const ImageDimensionData& images,
int lowerBound,
int maxDimension,
int targetArea,
bool powerOfTwo,
int padding,
int& resultWidth,
int& resultHeight,
size_t& amountFit,
AZStd::vector<AtlasCoordinates>& out);
//! Finds the tightest fit achievable by starting with the optimal thin solution and attempting to resize to be
//! a better shape
bool TryTighteningOptimal(const ImageDimensionData& images,
int smallestWidth,
int smallestHeight,
int maxDimension,
int targetArea,
bool powerOfTwo,
int padding,
int& resultWidth,
int& resultHeight,
size_t& amountFit,
AZStd::vector<AtlasCoordinates>& out);
//! Sorting logic for adding a slot to a sorted list in order to maintain increasing order
static void InsertInOrder(AZStd::vector<AtlasCoordinates>& slotList, AtlasCoordinates item);
//! Misc Logic For Estimating Target Shape
//! Returns the width of the widest element
static int GetWidest(const ImageDimensionData& imageList);
//! Returns the height of the tallest area
static int GetTallest(const ImageDimensionData& imageList);
};
//! Used for sorting ImageDimensions
static bool operator<(ImageDimension a, ImageDimension b);
//! Used to expose the ImageDimension in a pair to AZStd::Sort
static bool operator<(IndexImageDimension a, IndexImageDimension b);
//! Returns true if two coordinate sets overlap
static bool Collides(AtlasCoordinates a, AtlasCoordinates b);
//! Returns true if item collides with any object in list
static bool Collides(AtlasCoordinates item, AZStd::vector<AtlasCoordinates> list);
//! Returns the portion of the second item that overlaps with the first
static AtlasCoordinates GetOverlap(AtlasCoordinates a, AtlasCoordinates b);
//! Performs an operation that copies a pixel to the output
static void SetPixels(AZ::u8* dest, const AZ::u8* source, int destBytes);
//! Checks if we can insert an image into a slot
static bool CanInsert(AtlasCoordinates slot, ImageDimension image, int padding, int farRight, int farBot);
//! Adds the necessary padding to an Atlas Coordinate
static void AddPadding(AtlasCoordinates& slot, int padding, int farRight, int farBot);
}