Skip to content

UVAtlasCreate

Chuck Walbourn edited this page Jan 21, 2022 · 8 revisions
UVAtlas

Create a UV atlas for a mesh. This function performs the same operation as UVAtlasPartition then UVAtlasPack.

HRESULT UVAtlasCreate(
    const XMFLOAT3* positions, size_t nVerts,
    const void* indices, indexFormat, size_t nFaces,
    size_t maxChartNumber, float maxStretch,
    size_t width, size_t height,
    float gutter,
    const uint32_t *adjacency, const uint32_t *falseEdgeAdjacency,
    const float *pIMTArray,
    std::function<HRESULT(float percentComplete)> statusCallBack,
    float callbackFrequency,
    UVATLAS options,
    std::vector<UVAtlasVertex>& vMeshOutVertexBuffer,
    std::vector<uint8_t>& vMeshOutIndexBuffer,
    std::vector<uint32_t>* pvFacePartitioning = nullptr,
    std::vector<uint32_t>* pvVertexRemapArray = nullptr,
    float *maxStretchOut = nullptr, size_t *numChartsOut = nullptr);

Parameters

maxChartNumber: The maximum number of charts required for the atlas. If this is 0, it will be parameterized based solely on stretch. Note that this is not a hard limit, but the isochart will stop when a valid charting is found that is greater than or equal to this number.

maxStretch: The maximum amount of stretch. If 0.0, no stretching is allowed. If 1.0, then any amount of stretching is allowed.

width, height: The width and height of the texture the atlas will be used on.

gutter: The minimum distance, in texels between two charts on the atlas. This gets scaled by the width, so if gutter is 2.5, and it is used on a 512x512 texture, then the minimum distance will be 2.5 / 512 in u-v space.

falseEdgeAdjacency: a pointer to an array with 3 uint32_t per face, indicating at each face, whether an edge is a false edge or not (using the same ordering as the adjacency data structure). If this is nullptr, then it is assumed that there are no false edges. If not nullptr, then a non-false edge is indicated by -1 and a false edge is indicated by any other value (it is not required, but it may be useful for the caller to use the original adjacency value). This allows you to parameterize a mesh of quads, and the edges down the middle of each quad will not be cut when parameterizing the mesh.

pIMTArray: a pointer to an array with 3 floats per face, describing the integrated metric tensor for that face. This lets you control the way this triangle may be stretched in the atlas. The IMT passed in will be 3 floats (a,b,c) and specify a symmetric matrix (a b) that, given a vector (s,t), specifies the (b c) distance between a vector v1 and a vector v2 = v1 + (s,t) as sqrt((s, t) * M * (s, t)^T). In other words, this lets one specify the magnitude of the stretch in an arbitrary direction in u-v space.

For example if a = b = c = 1, then this scales the vector (1,1) by 2, and the vector (1,-1) by 0.

Note that this is multiplying the edge length by the square of the matrix, so if you want the face to stretch to twice its size with no shearing, the IMT value should be (2, 0, 2), which is just the identity matrix times 2.

This assumes you have an orientation for the triangle in some 2-D space. For UVAtlas, this space is created by letting S be the direction from the first to the second vertex, and T be the cross product between the normal and S.

See UVAtlasComputeIMTFromPerVertexSignal, UVAtlasComputeIMTFromSignal, UVAtlasComputeIMTFromTexture, UVAtlasComputeIMTFromPerTexelSignal

options: A combination of UVATLAS flags

  • UVATLAS_DEFAULT - By default, meshes with 25,000 or more faces default to "fast", otherwise they use "quality".
  • UVATLAS_GEODESIC_FAST - Uses approximations to improve charting speed at the cost of added stretch or more charts.
  • UVATLAS_GEODESIC_QUALITY - Provides better quality charts, but requires more time and memory than fast.
  • UVATLAS_LIMIT_MERGE_STRETCH - Enforces a threshold limit on merges that stretch the triangles too much.
  • UVATLAS_LIMIT_FACE_STRETCH - Enforces a upper-limit limit on face stretches. vMeshOutVertexBuffer, vMeshOutIndexBuffer: Resulting mesh data after UV atlasing which will have a different vertex order and new vertices due to duplication. The position data is replicated from the original vertices, but has unique uv data. The number of output faces is the same as the input positions array.

vMeshOutIndexBuffer: This is a 'binary blob' which contains either 16-bit uint16_t or 32-bit uint32_t indices depending on the value of indexFormat.

pvFacePartitioning: A pointer to an array of the final face-partitioning data. Can be nullptr.

pvVertexRemapArray: A pointer to the array of remapped vertices. Each array element identifies the original vertex each final vertex came from. The same original vertex can appear multiple times due to vertex splitting required, and the size of this vector is the same as the size of the vMeshOutVertexBuffer vector. Note: This is the reverse definition of DirectXMesh's vertex remap arrays, so you cannot use FinializeVB with this remap array. See UVAtlasApplyRemap.

maxStretchOut: Returns maximum stretch. Can be nullptr.

numChartsOut: Returns number of charts created. Can be nullptr.

Example

auto mesh = std::make_unique<WaveFrontReader<uint16_t>>();

if ( FAILED( mesh->Load( L"test.obj" ) ) )
   // Error

size_t nFaces = mesh->indices.size() / 3;
size_t nVerts = mesh->vertices.size();

std::unique_ptr<XMFLOAT3[]> pos( new XMFLOAT3[ nVerts ] );
for( size_t j = 0; j < nVerts; ++j )
   pos[ j ] = mesh->vertices[ j ].position;

std::unique_ptr<uint32_t[]> adj( new uint32_t[ mesh->indices.size() ] );
if ( FAILED( GenerateAdjacencyAndPointReps( &mesh->indices.front(), nFaces,
   pos.get(), nVerts, 0.f, nullptr, adj.get() ) ) )
   // Error

std::vector<UVAtlasVertex> vb;
std::vector<uint8_t> ib;
std::vector<uint32_t> remap;
HRESULT hr = UVAtlasCreate( pos.get(), nVerts,
   &mesh->indices.front(), DXGI_FORMAT_R16_UINT, nFaces,
   0, 0.f, 512, 512, 1.f,
   adj.get(), nullptr, nullptr,
   nullptr, UVATLAS_DEFAULT_CALLBACK_FREQUENCY,
   UVATLAS_DEFAULT, vb, ib,
   nullptr, &remap );
if (FAILED(hr))
   // Error

size_t nTotalVerts = vb.size();
std::unique_ptr<WaveFrontReader<uint16_t>::Vertex> newVB(
   new WaveFrontReader<uint16_t>::Vertex[ nTotalVerts ] );

hr = UVAtlasApplyRemap( &mesh->vertices.front(),
    sizeof(WaveFrontReader<uint16_t>::Vertex),
    nVerts, nTotalVerts,
    &remap.front(), newVB.get();
if (FAILED(hr))
   // Error

// Merge the UV data into the final VB
for( size_t j = 0; j < nTotalVerts; ++j )
    newVB[ j ].textureCoordinate = vb[ j ].uv;

auto newIB = reinterpret_cast<const uint16_t*>( &ib.front() );

This example makes use of DirectXMesh

Remarks

UVAtlas can partition mesh geometry two ways:

  • Based on the number of charts
  • Based on the maximum allowed stretch. If the maximum allowed stretch is 0, each triangle will likely be in its own chart

For Use

  • Universal Windows Platform apps
  • Windows desktop apps
  • Windows 11
  • Windows 10
  • Windows 8.1
  • Windows 7 Service Pack 1
  • Xbox One
  • Xbox Series X|S
  • Windows Subsystem for Linux

For Development

  • Visual Studio 2022
  • Visual Studio 2019 (16.11)
  • clang/LLVM v12 - v18
  • GCC 10.5, 11.4, 12.3
  • MinGW 12.2, 13.2
  • CMake 3.20

Related Projects

A python wrapper of UVAtlasTool

DirectXMesh

DirectXTex

DirectXMath

Tools

Test Suite

Content Exporter

DxCapsViewer

Clone this wiki locally