///////////////////////////////////////////////////////////////////////////
//
// System: Simplygon
// File: GeometryValidityTesting.cpp
// Language: C++
//
// Copyright (c) 2019 Microsoft. All rights reserved.
//
// This is private property, and it is illegal to copy or distribute in
// any form, without written authorization by the copyright owner(s).
//
///////////////////////////////////////////////////////////////////////////
//
// #Description#
//
// This example demonstrates how to look through a GeometryData-object and
// make sure all stored data inside is valid, and that it is stored
// correctly. The local function ValidateGeometry() gives an example of how
// the geometry can be validated manually.
//
// The IGeometryValidator class is also used on the geometry to display
// how you can simply use it to validate your geometries and get a string
// error message with description of any non-valid parts with the geometry.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
void RunExample( const std::string& readFrom );
bool CheckArray( IValueArray* arr, unsigned int tuplecount, unsigned int tuplesize );
bool CheckGeometry( IGeometryData* geom );
void ValidateGeometry( IGeometryData* geom );
int main( int argc, char* argv[] )
{
try
{
// Initiate
InitExample();
// Run the example code
std::string assetPath = GetAssetPath();
RunExample(assetPath + "testobject.obj");
// Deinitialize the SDK
DeinitExample();
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
return -1;
}
return 0;
}
void RunExample( const std::string& readFrom )
{
// Load from file
spWavefrontImporter objReader = sg->CreateWavefrontImporter();
objReader->SetImportFilePath( readFrom.c_str() );
if( !objReader->RunImport() )
throw std::exception("Failed to load input file!");
// Get the scene from the importer
spScene scene = objReader->GetScene();
// First running the loaded scene. Should be ok!
if( true )
{
std::cout << "Validating the original geometry." << std::endl;
spGeometryData geometry = scene->NewCombinedGeometry();
ValidateGeometry( geometry );
//Using the IGeometryValidator class
spGeometryValidator geometryValidator = sg->CreateGeometryValidator();
geometryValidator->ValidateGeometry( geometry );
rid error_value = geometryValidator->GetErrorValue();
rstring error_string = geometryValidator->GetErrorString();
printf( "\nIGeometryValidator: %s\n\n\n", error_string.GetText() );
}
// Now we mess up the Geometry some.
// Here we set the vertex index to an invalid index.
if( true )
{
std::cout << "\nValidating the geometry with invalid index." << std::endl;
spGeometryData geometry = scene->NewCombinedGeometry();
geometry->GetVertexIds()->SetItem( 73, geometry->GetVertexCount() + 1 );
// Lets validate again!
ValidateGeometry( geometry );
//Using the IGeometryValidator class
spGeometryValidator geometryValidator = sg->CreateGeometryValidator();
geometryValidator->ValidateGeometry( geometry );
rid error_value = geometryValidator->GetErrorValue();
rstring error_string = geometryValidator->GetErrorString();
printf( "\nIGeometryValidator: %s\n\n\n", error_string.GetText() );
}
// Another way to mess up the Geometry.
// This time we set an illegal tuplesize.
if( true )
{
std::cout << "\nValidating the geometry with invalid tuplesize." << std::endl;
spGeometryData geometry = scene->NewCombinedGeometry();
geometry->GetCoords()->SetTupleSize( 4 );
// Lets validate again!
ValidateGeometry( geometry );
//Using the IGeometryValidator class
spGeometryValidator geometryValidator = sg->CreateGeometryValidator();
geometryValidator->ValidateGeometry( geometry );
rid error_value = geometryValidator->GetErrorValue();
rstring error_string = geometryValidator->GetErrorString();
printf( "\nIGeometryValidator: %s\n\n\n", error_string.GetText() );
}
// Another way to mess up the Geometry.
// Create a degenerate triangle where two vertices in the triangle are the same vertex
if( true )
{
std::cout << "\nValidating the geometry with a degenerate triangle." << std::endl;
spGeometryData geometry = scene->NewCombinedGeometry();
geometry->GetVertexIds()->SetItem( 10 * 3 + 0, geometry->GetVertexIds()->GetItem( 10 * 3 + 1 ) );
//Using the IGeometryValidator class
spGeometryValidator geometryValidator = sg->CreateGeometryValidator();
geometryValidator->ValidateGeometry( geometry );
rid error_value = geometryValidator->GetErrorValue();
rstring error_string = geometryValidator->GetErrorString();
printf( "\nIGeometryValidator: %s\n\n\n", error_string.GetText() );
}
// Another way to mess up the Geometry.
// Create a zero area triangle
if( true )
{
std::cout << "\nValidating the geometry with a zero area triangle." << std::endl;
spGeometryData geometry = scene->NewCombinedGeometry();
geometry->GetCoords()->SetItem( 0, geometry->GetCoords()->GetItem( 3 ) );
geometry->GetCoords()->SetItem( 1, geometry->GetCoords()->GetItem( 4 ) );
geometry->GetCoords()->SetItem( 2, geometry->GetCoords()->GetItem( 5 ) );
//Using the IGeometryValidator class
spGeometryValidator geometryValidator = sg->CreateGeometryValidator();
geometryValidator->ValidateGeometry( geometry );
rid error_value = geometryValidator->GetErrorValue();
rstring error_string = geometryValidator->GetErrorString();
printf( "\nIGeometryValidator: %s\n\n\n", error_string.GetText() );
}
}
void ValidateGeometry( IGeometryData* geom )
{
//bool passedtest = CheckGeometry( geom );
spGeometryValidator validator = sg->CreateGeometryValidator();
bool passedtest = validator->ValidateGeometry( geom );
if( !passedtest )
std::cout << "Geometry failed test" << std::endl;
else
std::cout << "Geometry passed test" << std::endl;
}
bool CheckGeometry( IGeometryData* geom )
{
// Check geometry for consistency
unsigned int vertex_count = geom->GetVertexCount();
unsigned int triangle_count = geom->GetTriangleCount();
unsigned int corner_count = triangle_count * 3;
if( triangle_count < 1 || vertex_count < 3 )
{
std::cout << "Illegal mesh: too few vertices or triangles" << std::endl;
return false;
}
// Load indices for the vertices belonging to the 3 corners of each triangle.
// It should have the size 3 x triangle_count.
// TupleCount should be same as number of triangle-corners in the geometry, i.e 3 x triangle_count,
// and TupleSize should be 1. This one might be confusing, because its stored per-corner-basis, and not on
// per-triangle-basis, and that is why tuplesize is 1 and not 3.
spRidArray VertexIds = geom->GetVertexIds();
if( !CheckArray( VertexIds, corner_count, 1 ) )
return false;
// Check that the Coords-array is of correct size
spRealArray Coords = geom->GetCoords();
if( !CheckArray( Coords, vertex_count, 3 ) )
return false;
// Check that all VertexIds has correct index, i.e 0 <= index < vertex_count;
std::cout << "Checking if the indices contains valid values..." << std::endl;
rid index[3];
for( unsigned int i = 0; i < triangle_count; i++ )
{
for( int n = 0; n < 3; n++ )
{
index[n] = VertexIds->GetItem( i * 3 + n );
if( index[n] < 0 || rid( vertex_count ) <= index[n] )
{
std::cout << "error: Index for triangle: " << i << ", corner: " << n << " out of bounds!" << std::endl;
return false;
}
}
if( index[0] == index[1] ||
index[0] == index[2] ||
index[1] == index[2] )
{
std::cout << "warning: Triangle " << i << " contains same vertices atleast twice" << std::endl;
}
}
// Check texcoords, levels 0-3
spRealArray texcoordsarray;
for( int i = 0; i < 4; i++ )
{
texcoordsarray = geom->GetTexCoords( i );
if( !texcoordsarray.IsNull() )
{
if( !CheckArray( texcoordsarray, corner_count, 2 ) )
return false;
}
}
//Check normals
spRealArray Normals = geom->GetNormals();
if( !Normals.IsNull() )
{
if( !CheckArray( Normals, corner_count, 3 ) )
return false;
}
//Check material-ids
spRidArray Materials = geom->GetMaterialIds();
if( !Materials.IsNull() )
{
if( !CheckArray( Materials, triangle_count, 1 ) )
return false;
}
//Check group-ids
spRidArray Groups = geom->GetGroupIds();
if( !Groups.IsNull() )
{
if( !CheckArray( Groups, triangle_count, 1 ) )
return false;
}
return true;
}
bool CheckArray( IValueArray* arr, unsigned int tuplecount, unsigned int tuplesize )
{
rstring name = arr->GetName();
std::cout << "Checking size information on: " << name.GetText() << std::endl;
unsigned int count = arr->GetTupleCount();
if( tuplecount != count )
{
std::cout << "error: " << name.GetText() << " does not contain correct number of tuples" << std::endl;
std::cout << "tuplecount should be " << tuplecount << " but was " << count << std::endl;
return false;
}
count = arr->GetTupleSize();
if( tuplesize != count )
{
std::cout << "error: " << name.GetText() << " does not have the correct tuple-size" << std::endl;
std::cout << "tuplesize should be " << tuplesize << " but was " << count << std::endl;
return false;
}
count = arr->GetItemCount();
if( tuplecount * tuplesize != count )
{
std::cout << "error: " << name.GetText() << " does not contain correct number of items" << std::endl;
std::cout << "itemcount should be " << tuplecount * tuplesize << " but was " << count << std::endl;
return false;
}
return true;
}