You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
426 lines
14 KiB
C++
426 lines
14 KiB
C++
|
|
//-------------------------------------------------------------------------------------
|
||
|
|
// DirectXMeshValidate.cpp
|
||
|
|
//
|
||
|
|
// DirectX Mesh Geometry Library - Mesh validation
|
||
|
|
//
|
||
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||
|
|
// PARTICULAR PURPOSE.
|
||
|
|
//
|
||
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
|
//
|
||
|
|
// http://go.microsoft.com/fwlink/?LinkID=324981
|
||
|
|
//-------------------------------------------------------------------------------------
|
||
|
|
|
||
|
|
#include "DirectXMeshP.h"
|
||
|
|
|
||
|
|
using namespace DirectX;
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
|
||
|
|
//-------------------------------------------------------------------------------------
|
||
|
|
// Validates indices and optionally the adjacency information
|
||
|
|
//-------------------------------------------------------------------------------------
|
||
|
|
template<class index_t>
|
||
|
|
HRESULT ValidateIndices( _In_reads_(nFaces*3) const index_t* indices, _In_ size_t nFaces,
|
||
|
|
_In_ size_t nVerts, _In_reads_opt_(nFaces*3) const uint32_t* adjacency,
|
||
|
|
_In_ DWORD flags, _In_opt_ std::wstring* msgs )
|
||
|
|
{
|
||
|
|
bool result = true;
|
||
|
|
|
||
|
|
if ( !adjacency )
|
||
|
|
{
|
||
|
|
if ( flags & VALIDATE_BACKFACING )
|
||
|
|
{
|
||
|
|
if ( msgs )
|
||
|
|
*msgs += L"Missing adjacency information required to check for BACKFACING\n";
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( flags & VALIDATE_ASYMMETRIC_ADJ )
|
||
|
|
{
|
||
|
|
if ( msgs )
|
||
|
|
*msgs += L"Missing adjacency information required to check for ASYMMETRIC_ADJ\n";
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( !result )
|
||
|
|
return E_INVALIDARG;
|
||
|
|
}
|
||
|
|
|
||
|
|
for( size_t face = 0; face < nFaces; ++face )
|
||
|
|
{
|
||
|
|
// Check for values in-range
|
||
|
|
for( size_t point = 0; point < 3; ++point )
|
||
|
|
{
|
||
|
|
index_t i = indices[ face*3 + point ];
|
||
|
|
if ( i >= nVerts && i != index_t(-1) )
|
||
|
|
{
|
||
|
|
if ( !msgs )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
|
||
|
|
wchar_t buff[ 128 ];
|
||
|
|
swprintf_s( buff, L"An invalid index value (%u) was found on face %Iu\n", i, face );
|
||
|
|
*msgs += buff;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( adjacency )
|
||
|
|
{
|
||
|
|
uint32_t j = adjacency[ face*3 + point ];
|
||
|
|
if ( j >= nFaces && j != UNUSED32 )
|
||
|
|
{
|
||
|
|
if ( !msgs )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
|
||
|
|
wchar_t buff[ 128 ];
|
||
|
|
swprintf_s( buff, L"An invalid neighbor index value (%u) was found on face %Iu\n", j, face );
|
||
|
|
*msgs += buff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for unused faces
|
||
|
|
index_t i0 = indices[ face*3 ];
|
||
|
|
index_t i1 = indices[ face*3 + 1 ];
|
||
|
|
index_t i2 = indices[ face*3 + 2 ];
|
||
|
|
if ( i0 == index_t(-1)
|
||
|
|
|| i1 == index_t(-1)
|
||
|
|
|| i2 == index_t(-1) )
|
||
|
|
{
|
||
|
|
if ( flags & VALIDATE_UNUSED )
|
||
|
|
{
|
||
|
|
if ( i0 != i1
|
||
|
|
|| i0 != i2
|
||
|
|
|| i1 != i2 )
|
||
|
|
{
|
||
|
|
if ( !msgs )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
|
||
|
|
wchar_t buff[ 128 ];
|
||
|
|
swprintf_s( buff, L"An unused face (%Iu) contains 'valid' but ignored vertices (%u,%u,%u)\n", face, i0, i1, i2 );
|
||
|
|
*msgs += buff;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( adjacency )
|
||
|
|
{
|
||
|
|
for( size_t point = 0; point < 3; ++point )
|
||
|
|
{
|
||
|
|
uint32_t k = adjacency[ face*3 + point ];
|
||
|
|
if ( k != UNUSED32 )
|
||
|
|
{
|
||
|
|
if ( !msgs )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
|
||
|
|
wchar_t buff[ 128 ];
|
||
|
|
swprintf_s( buff, L"An unused face (%Iu) has a neighbor %u\n", face, k );
|
||
|
|
*msgs += buff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ignore unused triangles for remaining tests
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for degenerate triangles
|
||
|
|
if ( i0 == i1
|
||
|
|
|| i0 == i2
|
||
|
|
|| i1 == i2 )
|
||
|
|
{
|
||
|
|
if ( flags & VALIDATE_DEGENERATE )
|
||
|
|
{
|
||
|
|
if ( !msgs )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
|
||
|
|
index_t bad;
|
||
|
|
if ( i0 == i1 )
|
||
|
|
bad = i0;
|
||
|
|
else if ( i1 == i2 )
|
||
|
|
bad = i2;
|
||
|
|
else
|
||
|
|
bad = i0;
|
||
|
|
|
||
|
|
wchar_t buff[ 128 ];
|
||
|
|
swprintf_s( buff, L"A point (%u) was found more than once in triangle %Iu\n", bad, face );
|
||
|
|
*msgs += buff;
|
||
|
|
|
||
|
|
if ( adjacency )
|
||
|
|
{
|
||
|
|
for( size_t point = 0; point < 3; ++point )
|
||
|
|
{
|
||
|
|
uint32_t k = adjacency[ face*3 + point ];
|
||
|
|
if ( k != UNUSED32 )
|
||
|
|
{
|
||
|
|
result = false;
|
||
|
|
|
||
|
|
swprintf_s( buff, L"A degenerate face (%Iu) has a neighbor %u\n", face, k );
|
||
|
|
*msgs += buff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ignore degenerate triangles for remaining tests
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for symmetric neighbors
|
||
|
|
if ( ( flags & VALIDATE_ASYMMETRIC_ADJ ) && adjacency )
|
||
|
|
{
|
||
|
|
for( size_t point = 0; point < 3; ++point )
|
||
|
|
{
|
||
|
|
uint32_t k = adjacency[ face*3 + point ];
|
||
|
|
if ( k == UNUSED32 )
|
||
|
|
continue;
|
||
|
|
|
||
|
|
assert( k < nFaces );
|
||
|
|
|
||
|
|
uint32_t edge = find_edge<uint32_t>( &adjacency[ k * 3 ], uint32_t( face ) );
|
||
|
|
if ( edge >= 3 )
|
||
|
|
{
|
||
|
|
if ( !msgs )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
|
||
|
|
wchar_t buff[ 256 ];
|
||
|
|
swprintf_s( buff, L"A neighbor triangle (%u) does not reference back to this face (%Iu) as expected\n", k, face );
|
||
|
|
*msgs += buff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for duplicate neighbor
|
||
|
|
if ( ( flags & VALIDATE_BACKFACING ) && adjacency )
|
||
|
|
{
|
||
|
|
uint32_t j0 = adjacency[ face*3 ];
|
||
|
|
uint32_t j1 = adjacency[ face*3 + 1 ];
|
||
|
|
uint32_t j2 = adjacency[ face*3 + 2 ];
|
||
|
|
|
||
|
|
if ( ( j0 == j1 && j0 != UNUSED32 )
|
||
|
|
|| ( j0 == j2 && j0 != UNUSED32 )
|
||
|
|
|| ( j1 == j2 && j1 != UNUSED32 ) )
|
||
|
|
{
|
||
|
|
if ( !msgs )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
result = false;
|
||
|
|
|
||
|
|
uint32_t bad;
|
||
|
|
if ( j0 == j1 && j0 != UNUSED32 )
|
||
|
|
bad = j0;
|
||
|
|
else if ( j0 == j2 && j0 != UNUSED32 )
|
||
|
|
bad = j0;
|
||
|
|
else
|
||
|
|
bad = j1;
|
||
|
|
|
||
|
|
wchar_t buff[ 256 ];
|
||
|
|
swprintf_s( buff, L"A neighbor triangle (%u) was found more than once on triangle %Iu\n"
|
||
|
|
L"\t(likley problem is that two triangles share same points with opposite direction)\n", bad, face );
|
||
|
|
*msgs += buff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return result ? S_OK : E_FAIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//-------------------------------------------------------------------------------------
|
||
|
|
// Validates mesh contains no bowties (i.e. a vertex is the apex of two separate triangle fans)
|
||
|
|
//-------------------------------------------------------------------------------------
|
||
|
|
template<class index_t>
|
||
|
|
HRESULT ValidateNoBowties( _In_reads_(nFaces*3) const index_t* indices, _In_ size_t nFaces,
|
||
|
|
_In_ size_t nVerts, _In_reads_opt_(nFaces*3) const uint32_t* adjacency,
|
||
|
|
_In_opt_ std::wstring* msgs )
|
||
|
|
{
|
||
|
|
if ( !adjacency )
|
||
|
|
{
|
||
|
|
if ( msgs )
|
||
|
|
*msgs += L"Missing adjacency information required to check for BOWTIES\n";
|
||
|
|
|
||
|
|
return E_INVALIDARG;
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t tsize = ( sizeof(bool) * nFaces * 3 ) + ( sizeof(index_t) * nVerts * 2 ) + ( sizeof(bool) * nVerts );
|
||
|
|
std::unique_ptr<uint8_t[]> temp( new (std::nothrow) uint8_t[ tsize ] );
|
||
|
|
if ( !temp )
|
||
|
|
return E_OUTOFMEMORY;
|
||
|
|
|
||
|
|
auto faceSeen = reinterpret_cast<bool*>( temp.get() );
|
||
|
|
auto faceIds = reinterpret_cast<index_t*>( temp.get() + sizeof(bool) * nFaces * 3 );
|
||
|
|
auto faceUsing = reinterpret_cast<index_t*>( reinterpret_cast<uint8_t*>( faceIds ) + sizeof(index_t) * nVerts );
|
||
|
|
auto vertexBowtie = reinterpret_cast<bool*>( reinterpret_cast<uint8_t*>( faceUsing ) + sizeof(index_t) * nVerts );
|
||
|
|
|
||
|
|
memset( faceSeen, 0, sizeof(bool) * nFaces * 3 );
|
||
|
|
memset( faceIds, 0xFF, sizeof(index_t) * nVerts );
|
||
|
|
memset( faceUsing, 0, sizeof(index_t) * nVerts );
|
||
|
|
memset( vertexBowtie, 0, sizeof(bool) * nVerts );
|
||
|
|
|
||
|
|
orbit_iterator<index_t> ovi( adjacency, indices, nFaces );
|
||
|
|
|
||
|
|
bool result = true;
|
||
|
|
|
||
|
|
for( uint32_t face = 0; face < nFaces; ++face )
|
||
|
|
{
|
||
|
|
index_t i0 = indices[ face*3 ];
|
||
|
|
index_t i1 = indices[ face*3 + 1 ];
|
||
|
|
index_t i2 = indices[ face*3 + 2 ];
|
||
|
|
|
||
|
|
if ( i0 == i1
|
||
|
|
|| i0 == i2
|
||
|
|
|| i1 == i2 )
|
||
|
|
{
|
||
|
|
// ignore degenerate faces
|
||
|
|
faceSeen[ face * 3 ] = true;
|
||
|
|
faceSeen[ face * 3 + 1 ] = true;
|
||
|
|
faceSeen[ face * 3 + 2 ] = true;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
for( size_t point = 0; point < 3; ++point )
|
||
|
|
{
|
||
|
|
if ( faceSeen[ face * 3 + point ] )
|
||
|
|
continue;
|
||
|
|
|
||
|
|
faceSeen[ face * 3 + point ] = true;
|
||
|
|
|
||
|
|
index_t i = indices[ face*3 + point ];
|
||
|
|
ovi.initialize( face, i, orbit_iterator<index_t>::ALL );
|
||
|
|
ovi.moveToCCW();
|
||
|
|
while ( !ovi.done() )
|
||
|
|
{
|
||
|
|
uint32_t curFace = ovi.nextFace();
|
||
|
|
if ( curFace >= nFaces )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
uint32_t curPoint = ovi.getpoint();
|
||
|
|
if ( curPoint > 2 )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
faceSeen[ curFace*3 + curPoint ] = true;
|
||
|
|
|
||
|
|
uint32_t j = indices[ curFace * 3 + curPoint ];
|
||
|
|
|
||
|
|
if ( faceIds[ j ] == index_t(-1) )
|
||
|
|
{
|
||
|
|
faceIds[ j ] = index_t( face );
|
||
|
|
faceUsing[ j ] = index_t( curFace );
|
||
|
|
}
|
||
|
|
else if ( ( faceIds[ j ] != index_t( face ) ) && !vertexBowtie[ j ] )
|
||
|
|
{
|
||
|
|
// We found a (unique) bowtie!
|
||
|
|
|
||
|
|
if ( !msgs )
|
||
|
|
return E_FAIL;
|
||
|
|
|
||
|
|
if ( result )
|
||
|
|
{
|
||
|
|
// If this is the first bowtie found, add a quick explanation
|
||
|
|
*msgs += L"A bowtie was found. Bowties can be fixed by calling Clean\n"
|
||
|
|
L" A bowtie is the usage of a single vertex by two separate fans of triangles.\n"
|
||
|
|
L" The fix is to duplicate the vertex so that each fan has its own vertex.\n";
|
||
|
|
result = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
vertexBowtie[ j ] = true;
|
||
|
|
|
||
|
|
wchar_t buff[ 256 ];
|
||
|
|
swprintf_s( buff, L"\nBowtie found around vertex %u shared by faces %u and %u\n", j, curFace, faceUsing[ j ] );
|
||
|
|
*msgs += buff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return result ? S_OK : E_FAIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
};
|
||
|
|
|
||
|
|
namespace DirectX
|
||
|
|
{
|
||
|
|
|
||
|
|
//=====================================================================================
|
||
|
|
// Entry-points
|
||
|
|
//=====================================================================================
|
||
|
|
|
||
|
|
//-------------------------------------------------------------------------------------
|
||
|
|
_Use_decl_annotations_
|
||
|
|
HRESULT Validate( const uint16_t* indices, size_t nFaces, size_t nVerts,
|
||
|
|
const uint32_t* adjacency, DWORD flags, std::wstring* msgs )
|
||
|
|
{
|
||
|
|
if ( !indices || !nFaces || !nVerts )
|
||
|
|
return E_INVALIDARG;
|
||
|
|
|
||
|
|
if ( nVerts >= UINT16_MAX )
|
||
|
|
return E_INVALIDARG;
|
||
|
|
|
||
|
|
if ( ( uint64_t(nFaces) * 3 ) >= UINT32_MAX )
|
||
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
||
|
|
|
||
|
|
if ( msgs )
|
||
|
|
msgs->clear();
|
||
|
|
|
||
|
|
HRESULT hr = ValidateIndices<uint16_t>( indices, nFaces, nVerts, adjacency, flags, msgs );
|
||
|
|
if ( FAILED(hr) )
|
||
|
|
return hr;
|
||
|
|
|
||
|
|
if ( flags & VALIDATE_BOWTIES )
|
||
|
|
{
|
||
|
|
hr = ValidateNoBowties<uint16_t>( indices, nFaces, nVerts, adjacency, msgs );
|
||
|
|
if ( FAILED(hr) )
|
||
|
|
return hr;
|
||
|
|
}
|
||
|
|
|
||
|
|
return S_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//-------------------------------------------------------------------------------------
|
||
|
|
_Use_decl_annotations_
|
||
|
|
HRESULT Validate( const uint32_t* indices, size_t nFaces, size_t nVerts,
|
||
|
|
const uint32_t* adjacency, DWORD flags, std::wstring* msgs )
|
||
|
|
{
|
||
|
|
if ( !indices || !nFaces || !nVerts )
|
||
|
|
return E_INVALIDARG;
|
||
|
|
|
||
|
|
if ( nVerts >= UINT32_MAX )
|
||
|
|
return E_INVALIDARG;
|
||
|
|
|
||
|
|
if ( ( uint64_t(nFaces) * 3 ) >= UINT32_MAX )
|
||
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
||
|
|
|
||
|
|
if ( msgs )
|
||
|
|
msgs->clear();
|
||
|
|
|
||
|
|
HRESULT hr = ValidateIndices<uint32_t>( indices, nFaces, nVerts, adjacency, flags, msgs );
|
||
|
|
if ( FAILED(hr) )
|
||
|
|
return hr;
|
||
|
|
|
||
|
|
if ( flags & VALIDATE_BOWTIES )
|
||
|
|
{
|
||
|
|
hr = ValidateNoBowties<uint32_t>( indices, nFaces, nVerts, adjacency, msgs );
|
||
|
|
if ( FAILED(hr) )
|
||
|
|
return hr;
|
||
|
|
}
|
||
|
|
|
||
|
|
return S_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|