///////////////////////////////////////////////////////////////////////////
//
// System: Simplygon
// File: GeomorphExample.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# Shows how to use Geomorph. A scene is loaded, and some
// of the geometries in the scene are selected for reduction.
// The processed scene will contain reduced geometry mesh nodes,
// each of which in turn holds an additional child mesh node containing
// a geomorph geometry that shows how original vertices have moved during
// reduction. Blend geometries between original and geomorph geometries are
// then generated in steps of 1/10 and stored to file, to show how the
// morph-data can be used.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
void RunHighQualityReduction( const std::string& readFrom );
void MorphGeometry( spGeometryData Orig, spGeometryData Morph, spGeometryData Blend, real blend_val );
class ProgressObserver : public robserver
{
public:
virtual void Execute(
IObject * subject,
rid EventId,
void * EventParameterBlock,
unsigned int EventParameterBlockSize )
{
// only care for progress events
if( EventId == SG_EVENT_PROGRESS )
{
// get the progress in percent
int val = *((int *)EventParameterBlock);
// tell the process to continue
// this is required by the progress event
*((int *)EventParameterBlock) = 1;
// output the progress update
PrintProgressBar( val );
}
}
} progressObserver;
int main( int argc, char* argv[] )
{
try
{
InitExample();
// Before any specific processing starts, set global variables.
// Using Orthonormal method for calculating tangentspace.
sg->SetGlobalSetting("DefaultTBNType", SG_TANGENTSPACEMETHOD_ORTHONORMAL);
std::string assetPath = GetAssetPath();
// Run HQ reduction example, reducing a single geometry to a single LOD
printf("Running HQ reduction... \n");
PrintProgressBar(0);
RunHighQualityReduction(assetPath + "SimplygonMan/SimplygonMan.obj");
printf("\nDone.\n\n");
// Done!
printf("All LODs complete, shutting down...");
DeinitExample();
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
return -1;
}
return 0;
}
void RunHighQualityReduction( const std::string& readFrom )
{
// Load input geometry from file
spWavefrontImporter objReader = sg->CreateWavefrontImporter();
objReader->SetExtractGroups( true ); //This makes the .obj reader import each group into a separate scene mesh node
objReader->SetImportFilePath( readFrom.c_str() );
if( !objReader->RunImport() )
throw std::exception("Failed to load input file!");
spScene lodScene;
spScene originalScene;
spScene blendScene;
originalScene = objReader->GetScene();
// Create a selection-set with all mesh nodes.
int mesh_nodes_id = originalScene->SelectNodes( "ISceneMesh" );
spSelectionSet mesh_nodes = originalScene->GetSelectionSetTable()->GetSelectionSet( mesh_nodes_id );
// Copy the original scene. One to be used for morphing the geometry, and one to send to the ReductionProcessor.
lodScene = originalScene->NewCopy();
blendScene = originalScene->NewCopy();
// Select all nodes that contain the word "dman", which all parts that belong to the characters in the scene do.
spSelectionSet selectionSet = sg->CreateSelectionSet();
std::string dman_string = std::string( "dman" );
for( uint i = 0; i < mesh_nodes->GetItemCount(); ++i )
{
std::string mesh_name = std::string( lodScene->GetNodeByGUID( mesh_nodes->GetItem( i ) )->GetName() );
if( mesh_name.find( dman_string ) != std::string::npos )
{
spSceneMesh mesh = Cast<ISceneMesh>( lodScene->GetNodeByGUID( mesh_nodes->GetItem( i ) ) );
selectionSet->AddItem( mesh->GetNodeGUID() );
}
}
rid selection_set_id = lodScene->GetSelectionSetTable()->AddSelectionSet( selectionSet );
// Create the reduction-processor, and set the geometry to reduce
spReductionProcessor reductionProcessor = sg->CreateReductionProcessor();
reductionProcessor->SetScene( lodScene );
///////////////////////////////////////////////////////////////////////////////////////////////
// SETTINGS - Most of these are set to the same value by default, but are set anyway for clarity
// The reduction settings object contains settings pertaining to the actual decimation
spReductionSettings reductionSettings = reductionProcessor->GetReductionSettings();
reductionSettings->SetProcessSelectionSetID( selection_set_id );
reductionSettings->SetCreateGeomorphGeometry( true ); // Set to create a GeomorphGeometry. Fetched after processing with "reductionProcessor->GetGeomorphGeometry();"
reductionSettings->SetUseSymmetryQuadRetriangulator( false ); // Cant be used at the same time as creating GeomorphGeometry, since the connectivity to the triangles will change and muck things up.
reductionProcessor->GetNormalCalculationSettings()->SetReplaceNormals( true );
reductionProcessor->GetNormalCalculationSettings()->SetHardEdgeAngleInRadians( 3.14159f * 60.f / 180.0f );
// Reduce the scene to 10% of the original triangle-count.
reductionSettings->SetStopCondition( SG_STOPCONDITION_ANY );
reductionSettings->SetReductionTargets( SG_REDUCTIONTARGET_TRIANGLERATIO );
reductionSettings->SetTriangleRatio( 0.1f );
// The repair settings object contains settings for the pre-processing block
spRepairSettings repairSettings = reductionProcessor->GetRepairSettings();
repairSettings->SetWeldDist( 0.0f );
repairSettings->SetUseTJunctionRemover( false ); // Cant be used at the same time as creating GeomorphGeometry, since new triangles are created and then we cant blend them.
//END SETTINGS
///////////////////////////////////////////////////////////////////////////////////////////////
reductionProcessor->AddObserver( &progressObserver, SG_EVENT_PROGRESS );
// Run the actual processing. After this, the set geometry will have been reduced according to the settings
reductionProcessor->RunProcessing();
// Selection set to collect geomorph nodes in scene
spSelectionSet geomorphSelectionSet = sg->CreateSelectionSet();
// Morph the geometry, 10 % each step.
for( float blend_val = 0.0f; blend_val <= 1.0f; blend_val += 0.1f )
{
// The LOD scene contains morphed geometries, placed as children of their respective reduced geometry mesh node.
// The name of a morph mesh node is the same as its parent, with "_geomorph" added.
for( uint i = 0; i < mesh_nodes->GetItemCount(); ++i )
{
spSceneMesh mesh = Cast<ISceneMesh>( originalScene->GetNodeByGUID( mesh_nodes->GetItem( i ) ) );
spSceneMesh lod = Cast<ISceneMesh>( lodScene->GetNodeByGUID( mesh_nodes->GetItem( i ) ) );
spSceneMesh morph = NULL;
spSceneMesh blend = Cast<ISceneMesh>( blendScene->GetNodeByGUID( mesh_nodes->GetItem( i ) ) );
uint num_children = lod->GetChildCount();
for( uint j = 0; j < num_children; j++ )
{
std::string child_name = std::string( lod->GetChild( j )->GetName() );
std::string morph_name = std::string( "_geomorph" );
if( child_name.find( morph_name ) != std::string::npos )
{
morph = Cast<ISceneMesh>( lod->GetChild( j ) );
//printf("Found morph mesh for LOD ");
//printf( lod->GetName() );
//printf("\n");
// Add to selection set
geomorphSelectionSet->AddItem( morph->GetNodeGUID() );
// Morph the geometry and store it into the mesh from the blend-scene.
MorphGeometry( mesh->GetGeometry(), morph->GetGeometry(), blend->GetGeometry(), blend_val );
break;
}
}
}
// Save the blend-scene to file.
spWavefrontExporter exp = sg->CreateWavefrontExporter();
exp->SetScene( blendScene );
std::string output_name = std::string( "BlendedGeometry" ) + std::to_string( blend_val ) + ".obj";
exp->SetExportFilePath( output_name.c_str() );
exp->RunExport();
}
// Also store the LOD to file.
// Remove all geomorph nodes first.
rid geomorph_selection_set_id = lodScene->GetSelectionSetTable()->AddSelectionSet( geomorphSelectionSet );
lodScene->RemoveSceneNodes( geomorph_selection_set_id );
spWavefrontExporter exp = sg->CreateWavefrontExporter();
exp->SetScene( lodScene );
std::string output_name = "LodGeom.obj";
exp->SetExportFilePath( output_name.c_str() );
exp->RunExport();
//Done! LOD created.
}
void ArrayBlend( spRealArray TargetArray, spRealArray OrigArray, spRealArray MorphArray, real blend_val )
{
for( uint i = 0; i < TargetArray->GetItemCount(); i++ )
{
real tmp = OrigArray->GetItem( i )*blend_val + MorphArray->GetItem( i )*(1.0f - blend_val);
TargetArray->SetItem( i, tmp );
}
}
void MorphGeometry( spGeometryData Orig, spGeometryData Morph, spGeometryData Blend, real blend_val )
{
spRealArray TargetArray, OrigArray, MorphArray;
spRidArray TargetVertexIds, OrigVertexIds, MorphVertexIds;
//-------------------------------------------------------------
// Morph the Coords
//-------------------------------------------------------------
TargetArray = Blend->GetCoords();
OrigArray = Orig->GetCoords();
MorphArray = Morph->GetCoords();
TargetVertexIds = Blend->GetVertexIds();
OrigVertexIds = Orig->GetVertexIds();
MorphVertexIds = Morph->GetVertexIds();
// For each corner:
for( uint c = 0; c < TargetVertexIds->GetItemCount(); ++c )
{
// For each x,z,y value in each coordinate:
for( uint d = 0; d < 3; d++ )
{
real tmp = OrigArray->GetItem( OrigVertexIds->GetItem( c ) * 3 + d )*blend_val + MorphArray->GetItem( MorphVertexIds->GetItem( c ) * 3 + d )*(1.0f - blend_val);
TargetArray->SetItem( TargetVertexIds->GetItem( c ) * 3 + d, tmp );
}
}
//-------------------------------------------------------------
// Morph the UVs
//-------------------------------------------------------------
TargetArray = Blend->GetTexCoords( 0 );
OrigArray = Orig->GetTexCoords( 0 );
MorphArray = Morph->GetTexCoords( 0 );
if( TargetArray )
ArrayBlend( TargetArray, OrigArray, MorphArray, blend_val );
//-------------------------------------------------------------
// Morph the Normals
//-------------------------------------------------------------
TargetArray = Blend->GetNormals();
OrigArray = Orig->GetNormals();
MorphArray = Morph->GetNormals();
if( TargetArray )
ArrayBlend( TargetArray, OrigArray, MorphArray, blend_val );
//-------------------------------------------------------------
// Morph the Tangents
//-------------------------------------------------------------
TargetArray = Blend->GetTangents( 0 );
OrigArray = Orig->GetTangents( 0 );
MorphArray = Morph->GetTangents( 0 );
if( TargetArray )
ArrayBlend( TargetArray, OrigArray, MorphArray, blend_val );
//-------------------------------------------------------------
// Morph the Bitangents
//-------------------------------------------------------------
TargetArray = Blend->GetBitangents( 0 );
OrigArray = Orig->GetBitangents( 0 );
MorphArray = Morph->GetBitangents( 0 );
if( TargetArray )
ArrayBlend( TargetArray, OrigArray, MorphArray, blend_val );
}