// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include #include "Sampling/MeshBaseBaker.h" #include "Sampling/MeshMapEvaluator.h" #include "Sampling/MeshSurfaceSampler.h" #include "Image/ImageBuilder.h" #include "Image/ImageDimensions.h" #include "Image/BoxFilter.h" #include "Image/BCSplineFilter.h" #define UE_API DYNAMICMESH_API namespace UE { namespace Geometry { class FImageOccupancyMap; class FImageTile; class FMeshMapTileBuffer; class FMeshMapBaker : public FMeshBaseBaker { public: // // Bake // /** Process all bakers to generate image results for each. */ UE_API void Bake(); /** Add a baker to be processed. */ UE_API int32 AddEvaluator(const TSharedPtr& Eval); /** @return the evaluator at the given index. */ UE_API FMeshMapEvaluator* GetEvaluator(int32 EvalIdx) const; /** @return the number of bake evaluators on this baker. */ UE_API int32 NumEvaluators() const; /** Reset the list of bakers. */ UE_API void Reset(); /** @return the bake result image for a given baker index. */ UE_API const TArrayView>> GetBakeResults(int32 EvalIdx); /** @return true if we should abort calculation */ TFunction CancelF = []() { return false; }; /** Function to call after evaluator data is written to the final image, but before any gutter processing occurs */ TFunction>>&)> PostWriteToImageCallback = [](TArray>>& PostWriteToImageBakeResults) {}; /* Function to call for each interior sample */ TFunction InteriorSampleCallback = [](bool bSampleValid, const FMeshMapEvaluator::FCorrespondenceSample& Sample, const FVector2d& UVPosition, const FVector2i& ImageCoords) {}; /** * @param ImageCoords the output image coordinates to be evaluated * @param UV the target mesh UV coordinates to be evaluated * @param TriID the target mesh triangle ID to be evaluated * @return a weight (clamped by the baker to the range [0,1]) used to combine evaluator sample values with evaluator * defaults. For evaluators using EAccumulateMode::Overwrite the default is used when weight == 0, and the value is * used otherwise. For evaluators using EAccumulateMode::Add the sample's value and default are blended using the * expression: `weight * value + (1 - weight) * default` */ TFunction SampleFilterF = nullptr; // // Parameters // enum class EBakeFilterType { None, Box, BSpline, MitchellNetravali }; UE_API void SetDimensions(FImageDimensions DimensionsIn); UE_API void SetGutterEnabled(bool bEnabled); UE_API void SetGutterSize(int32 GutterSizeIn); UE_API void SetSamplesPerPixel(int32 SamplesPerPixelIn); UE_API void SetFilter(EBakeFilterType FilterTypeIn); UE_API void SetTileSize(int TileSizeIn); FImageDimensions GetDimensions() const { return Dimensions; } bool GetGutterEnabled() const { return bGutterEnabled; } int32 GetGutterSize() const { return GutterSize; } int32 GetSamplesPerPixel() const { return SamplesPerPixel; } EBakeFilterType GetFilter() const { return FilterType; } int32 GetTileSize() const { return TileSize; } /** * Computes the connected UV triangles and returns an array containing * the mapping from triangle ID to unique UV chart ID. If the mesh * has no UVs, the UVCharts will be initialized to 0. * * @param Mesh the mesh to compute UV charts. * @param MeshUVCharts the triangle ID to UV Chart ID array. */ static UE_API void ComputeUVCharts(const FDynamicMesh3& Mesh, TArray& MeshUVCharts); /** * Set an a Triangle ID to UV Chart ID array for TargetMesh. * If this is not set, then the baker will compute it as part of Bake(). * Since ComputeUVCharts() is non-trivial, this method is intended * to allow a client to externally cache the result of ComputeUVCharts * to minimize the overhead per bake. * * @param UVChartsIn the TriID to UVChartID map */ void SetTargetMeshUVCharts(TArray* UVChartsIn) { TargetMeshUVCharts = UVChartsIn; } /** @return the Triangle ID to UV Chart ID mapping */ const TArray* GetTargetMeshUVCharts() const { return TargetMeshUVCharts; } // // Analytics // struct FBakeAnalytics { double TotalBakeDuration = 0.0; double WriteToImageDuration = 0.0; double WriteToGutterDuration = 0.0; std::atomic NumSamplePixels = 0; std::atomic NumGutterPixels = 0; void Reset() { TotalBakeDuration = 0.0; WriteToImageDuration = 0.0; WriteToGutterDuration = 0.0; NumSamplePixels = 0; NumGutterPixels = 0; } }; FBakeAnalytics BakeAnalytics; protected: /** Evaluate this sample. */ UE_API void BakeSample( FMeshMapTileBuffer& TileBuffer, const FMeshMapEvaluator::FCorrespondenceSample& Sample, const FVector2d& QueryUVPosition, const FVector2i& ImageCoords, const FImageOccupancyMap& OccupancyMap); /** Initialize evaluation contexts and precompute data for bake evaluation. */ UE_API void InitBake(); /** Initialize bake sample default floats and colors. */ UE_API void InitBakeDefaults(); /** Initialize filter */ UE_API void InitFilter(); protected: const bool bParallel = true; FDynamicMesh3 FlatMesh; FMeshSurfaceUVSampler MeshUVSampler; FImageDimensions Dimensions = FImageDimensions(128, 128); /** @return evaluator ids with the corresponding mode */ const TArray& EvaluatorIdsForMode(FMeshMapEvaluator::EAccumulateMode Mode) const { return BakeAccumulateLists[static_cast(Mode)]; } /** * If true, the baker will pad the baked content past the UV borders by GutterSize. * This is useful to minimize artifacts when filtering or mipmapping. */ bool bGutterEnabled = true; /** The pixel distance (in texel diagonal length) to pad baked content past the UV borders. */ int32 GutterSize = 4; /** The number of samples to evaluate per pixel. */ int32 SamplesPerPixel = 1; /** The square dimensions for tiled processing of the output image(s). */ int32 TileSize = 32; /** The amount of padding for tiled processing of the output image(s). */ int32 TilePadding = 2; /** The pixel distance around the sample texel to be considered by the filter. [0, TilePadding] */ int32 FilterKernelSize = 0; /** The texture filter type. */ EBakeFilterType FilterType = EBakeFilterType::BSpline; /** Texture filters */ static UE_API FBoxFilter BoxFilter; static UE_API FBSplineFilter BSplineFilter; static UE_API FMitchellNetravaliFilter MitchellNetravaliFilter; /** Texture filter function */ using TextureFilterFn = float(*)(const FVector2d& Dist); TextureFilterFn TextureFilterEval = nullptr; using IsInFilterRegionFn = bool(*)(const FVector2d& Dist); IsInFilterRegionFn IsInFilterRegionEval = nullptr; template static float EvaluateFilter(const FVector2d& Dist); template static bool EvaluateIsInFilterRegion(const FVector2d& Dist); /** The total size of the temporary float buffer for BakeSample. */ int32 BakeSampleBufferSize = 0; /** The list of evaluators to process. */ TArray> Bakers; /** Evaluation contexts for each mesh evaluator. */ TArray BakeContexts; /** Lists of Bake indices for each accumulation mode. */ TArray> BakeAccumulateLists; /** Array of default values/colors per BakeResult. */ TArray BakeDefaults; TArray BakeDefaultColors; /** Offsets per Baker into the BakeResults array.*/ TArray BakeOffsets; /** Offsets per BakeResult into the BakeSample buffer.*/ TArray BakeSampleOffsets; /** Array of bake result images. */ TArray>> BakeResults; /** * Array of TargetMesh triangle ID to UV chart ID mapping. * Can be optionally provided by the client. If not provided, * will be computed as part of the bake. */ TArray* TargetMeshUVCharts = nullptr; /** * Local Array of TargetMesh triangle ID to UV chart ID mapping. * This will be populated only if not provided by the client via * TargetMeshUVCharts. */ TArray TargetMeshUVChartsLocal; }; } // end namespace UE::Geometry } // end namespace UE #undef UE_API